From 951d7771f35d762f2299380d74dcb3ef5d345ec2 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 9 Aug 2016 15:40:50 -0400 Subject: [PATCH 001/721] tonejs synth for play note blocks --- playground/index.html | 19 +++++++++++-- src/blocks/scratch3_sound.js | 53 ++++++++++++++++++++++++++++++++++++ src/engine/runtime.js | 3 +- src/worker.js | 40 +++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 src/blocks/scratch3_sound.js diff --git a/playground/index.html b/playground/index.html index bcfb4426..bf2a2181 100644 --- a/playground/index.html +++ b/playground/index.html @@ -248,7 +248,7 @@ - + @@ -278,6 +278,7 @@ + @@ -285,11 +286,23 @@ - - 0.5 + + + + + + + + + + + + + + diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js new file mode 100644 index 00000000..0c78f027 --- /dev/null +++ b/src/blocks/scratch3_sound.js @@ -0,0 +1,53 @@ +function Scratch3SoundBlocks(runtime) { + /** + * The runtime instantiating this block package. + * @type {Runtime} + */ + this.runtime = runtime; +} + +/** + * Retrieve the block primitives implemented by this package. + * @return {Object.} Mapping of opcode to Function. + */ +Scratch3SoundBlocks.prototype.getPrimitives = function() { + return { + 'sound_playsound': this.playSound, + 'sound_playnoteforbeats': this.playNote, + 'sound_setkey' : this.setKey, + 'sound_scales_menu' : this.scalesMenu, + 'sound_roots_menu' : this.rootsMenu, + 'sound_beats_menu' : this.beatsMenu, + }; +}; + +Scratch3SoundBlocks.prototype.playSound = function (args, util) { + self.postMessage({method: 'beep'}); +}; + +Scratch3SoundBlocks.prototype.playNote = function (args, util) { + self.postMessage({method: 'playnote', note:args.NOTE, beats:args.BEATS}); + return new Promise(function(resolve) { + setTimeout(function() { + resolve(); + }, 1000 * args.BEATS); + }); +}; + +Scratch3SoundBlocks.prototype.setKey = function (args, util) { + self.postMessage({method: 'setkey', root:args.ROOT, scale:args.SCALE}); +}; + +Scratch3SoundBlocks.prototype.scalesMenu = function (args, util) { + return args.SCALE; +}; + +Scratch3SoundBlocks.prototype.rootsMenu = function (args, util) { + return args.ROOT; +}; + +Scratch3SoundBlocks.prototype.beatsMenu = function (args, util) { + return args.BEATS; +}; + +module.exports = Scratch3SoundBlocks; diff --git a/src/engine/runtime.js b/src/engine/runtime.js index f92a91db..a7728905 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -8,7 +8,8 @@ var defaultBlockPackages = { 'scratch3_event': require('../blocks/scratch3_event'), 'scratch3_looks': require('../blocks/scratch3_looks'), 'scratch3_motion': require('../blocks/scratch3_motion'), - 'scratch3_operators': require('../blocks/scratch3_operators') + 'scratch3_operators': require('../blocks/scratch3_operators'), + 'scratch3_sound': require('../blocks/scratch3_sound') }; /** diff --git a/src/worker.js b/src/worker.js index 70e6da5c..b154664c 100644 --- a/src/worker.js +++ b/src/worker.js @@ -1,5 +1,6 @@ var EventEmitter = require('events'); var util = require('util'); +var Tone = require('tone'); function VirtualMachine () { if (!window.Worker) { @@ -11,8 +12,47 @@ function VirtualMachine () { EventEmitter.call(instance); instance.vmWorker = new Worker('../vm.js'); + // MUSIC STUFF by ericr + var options = {modulationEnvelope:{attack:0.1}}; + var synth = new Tone.PolySynth(6, Tone.Synth).toMaster(); + var tone = new Tone(); + + var scales = { + 'MAJOR' : [0,2,4,5,7,9,11], + 'MINOR' : [0,2,3,5,7,8,10], + 'PENTATONIC': [0, 2, 4, 7, 9], + 'CHROMATIC' : [0,1,2,3,4,5,6,7,8,9,10,11], + }; + + var currentScale = scales['MAJOR']; + var rootNote = 60; + + function scaleNoteToMidiNote(scaleNote, scale, root) { + var scaleIndex = (Math.round(scaleNote) - 1) % scale.length; + if (scaleIndex < 0) { + scaleIndex += scale.length; + } + var octave = Math.floor((scaleNote - 1) / scale.length); + var midiNote = root + (octave * 12) + scale[scaleIndex]; + return midiNote; + } + + function midiToFreq(midiNote) { + var freq = tone.intervalToFrequencyRatio(midiNote - 60) * 261.63; // 60 is C4 + return freq; + } + // onmessage calls are converted into emitted events. instance.vmWorker.onmessage = function (e) { + if (e.data.method == 'playnote') { + var midiNote = scaleNoteToMidiNote(e.data.note, currentScale, rootNote); + var freq = midiToFreq(midiNote); + synth.triggerAttackRelease(freq, e.data.beats); + } + if (e.data.method == 'setkey') { + rootNote = e.data.root + 60; + currentScale = scales[e.data.scale]; + } instance.emit(e.data.method, e.data); }; From 8c40e9c38386afe571557a78618a8f7a67dbc216 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 11 Aug 2016 16:47:01 -0400 Subject: [PATCH 002/721] play sound, play drum and audio effects experiments --- playground/index.html | 90 ++++++++++++--- playground/sounds/boing.mp3 | Bin 0 -> 2888 bytes playground/sounds/cave.mp3 | Bin 0 -> 62098 bytes playground/sounds/drip_drop.mp3 | Bin 0 -> 22165 bytes playground/sounds/drum_machine.mp3 | Bin 0 -> 41803 bytes playground/sounds/eggs.mp3 | Bin 0 -> 120673 bytes playground/sounds/hi_tun_tabla.mp3 | Bin 0 -> 6138 bytes playground/sounds/high_conga.mp3 | Bin 0 -> 3095 bytes playground/sounds/lo_gliss_tabla.mp3 | Bin 0 -> 2758 bytes playground/sounds/meow.mp3 | Bin 0 -> 7314 bytes playground/sounds/sidestick_snare.mp3 | Bin 0 -> 1588 bytes playground/sounds/small_cowbell.mp3 | Bin 0 -> 3460 bytes playground/sounds/snare_drum.mp3 | Bin 0 -> 1457 bytes playground/sounds/splash cymbal.mp3 | Bin 0 -> 4008 bytes playground/sounds/zoop.mp3 | Bin 0 -> 1981 bytes src/blocks/scratch3_sound.js | 63 +++++++++- src/worker.js | 159 ++++++++++++++++++++++++-- 17 files changed, 280 insertions(+), 32 deletions(-) create mode 100644 playground/sounds/boing.mp3 create mode 100644 playground/sounds/cave.mp3 create mode 100644 playground/sounds/drip_drop.mp3 create mode 100644 playground/sounds/drum_machine.mp3 create mode 100644 playground/sounds/eggs.mp3 create mode 100644 playground/sounds/hi_tun_tabla.mp3 create mode 100644 playground/sounds/high_conga.mp3 create mode 100644 playground/sounds/lo_gliss_tabla.mp3 create mode 100644 playground/sounds/meow.mp3 create mode 100644 playground/sounds/sidestick_snare.mp3 create mode 100644 playground/sounds/small_cowbell.mp3 create mode 100644 playground/sounds/snare_drum.mp3 create mode 100644 playground/sounds/splash cymbal.mp3 create mode 100644 playground/sounds/zoop.mp3 diff --git a/playground/index.html b/playground/index.html index bf2a2181..79bd85f5 100644 --- a/playground/index.html +++ b/playground/index.html @@ -248,17 +248,43 @@ + - - + + + + - - + + + + + + + + + + + + 1 + + + + + + + + + 1 + + + + @@ -266,11 +292,11 @@ - - 0.25 + + @@ -279,6 +305,14 @@ + + + + 1 + + + + @@ -291,17 +325,41 @@ - - - - - - - - - - + + + + + + + + + + + + + + + + + + 100 + + + + + + + + + + + 10 + + + + + diff --git a/playground/sounds/boing.mp3 b/playground/sounds/boing.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..9542bd23fcdb2fe5de2ee66635c30c4d2ab2dedb GIT binary patch literal 2888 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 literal 0 HcmV?d00001 diff --git a/playground/sounds/cave.mp3 b/playground/sounds/cave.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..b5643a2404237b89478a2dd8e851fd637d1e2c0f GIT binary patch literal 62098 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 literal 0 HcmV?d00001 diff --git a/playground/sounds/drum_machine.mp3 b/playground/sounds/drum_machine.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..69892f3607487b04b0bc4657b9f40cad5e9335ff GIT binary patch literal 41803 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

Z7Q_8Sgh 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

DPGrmql5Xa;T|Bu7HF|9~3N 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 Ks;a80s;a6tbKwI3 literal 0 HcmV?d00001 diff --git a/playground/sounds/hi_tun_tabla.mp3 b/playground/sounds/hi_tun_tabla.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..78e4b6475763cdf24550d9553f96222781c6e873 GIT binary patch literal 6138 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 R>5=~1Mg_iJvHYLPe*nTs1K0on literal 0 HcmV?d00001 diff --git a/playground/sounds/meow.mp3 b/playground/sounds/meow.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..a16da03be0f7715ee63694356b893fb0e90c9671 GIT binary patch literal 7314 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 literal 0 HcmV?d00001 diff --git a/playground/sounds/sidestick_snare.mp3 b/playground/sounds/sidestick_snare.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..8160103726dbd72e170197368ebdcf594b5d8410 GIT binary patch literal 1588 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 literal 0 HcmV?d00001 diff --git a/playground/sounds/snare_drum.mp3 b/playground/sounds/snare_drum.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..9dc6db7abc7c839192fa6f3dc11496516f6ab02f GIT binary patch literal 1457 zcmezWx#0){3`At+r32agK+MU&z`Buv6#-lkpa21;BET*Lcz^(35#SdB{6_$wT>(Ch zzOKf4mKH@Uc`&D#aHz0}Dl#xW0J(!L?xh$^f&l&hvjE6H%zqdi7#PZ?htDjZ79QfV z;kaql|IZ7K@Vee9`z}B80E5O9MY%KK?|H3ewOJm#+wyG20ac$O7MIS3r3VgO({)(k zwPqq$grvcfl__k`bGvfT#YAZxXc@ z&cEyDck8$mk5=f6Q)(Yimuub&eWp}m5`1lK=w|g9OQ+u1sJT^p`qgP!zVX_H(vt6I z#=N~1ZMOD&ef;kEMd{&{Sxb3u)$ChWx;^UeYO{AY-&>w8`+RP>PTa=&pfkFsbPkJc z_gwSd;d|~W&F%TS_qRnp@43tNCS^?jDhG!Bzkk2i)&GB3cJ@`a*U#mvziruSppg-! zEXL8O`GDc%3|*~m&DB{+Eds%s&Owj=1CxlN$(g8EUvDNbYG>^%7vt5BnSO82V~6%h z%~ERxJ$W6=xIgCRW@P^*kcu@xfg;%crGw`==seHPW& zdtG6zWQ+kv!YZF%T}taqTYLfnh4u#b^6yks>J)n8%bycsCy_i;%}w%$%~jSM-m+}1 z}0DcYidm9)-2aF+PD6xvf+!PulDb;Ut4u%)#}4b zKjk_0rEZ_mS=t=6D$wBq40Ij3iEV+$B!0{{3(tW{4>ke>h(PFjCy(T(ha_?SvRKZMaOJBw$Rr@W7YyC z(d2*yhO6WqT&@}=hiZwg`g?N<%eG`4jblrHUJ;$YZPjXKu4`-3GVZquhxbY+r`fIg zD-^@S!lK!1te112f61w(W~Fb^td{;;bu1!4K(s41G&KIz>$jKElDn8x9g}8pb83NH z&7dXysaoU^yNvtHRV^jjQDQ4?Pj3=q;S)CYm_G9;*NkUueKT}E^3L+M^_y0+b&*ke zmfp3v>`U*C#7yC9h&VjO$@*C*f95*YITLbhLu?(hBSqIqOx3InTXZeUcbUf1DPq|X z|DSD*YRkW_`sm8CTTv%hdI!9?*_df&`v3a>SJNevWo*7iF~#KfdiRK2t#vs!HKJrz zP~Q4g|K~6%H?|f8Gjt}uy0EA>IkC08X_01%@xs#opB;`c2>jr(krMzGCI4GojOT(1 ztUu1Gd6AzilQ%6`z5mGh|Nq|zO?|B+%B$PWTG8?8;L6AAb$A&=N~E+(Bn6cPnH?^O ps0rC{&1H4**MSvqdPb{|0jRJN2NhPJLJe7B6-Y(}DC2=40{{r3s=xpM literal 0 HcmV?d00001 diff --git a/playground/sounds/splash cymbal.mp3 b/playground/sounds/splash cymbal.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..3026cb559afb0742affc9c58500982958af02be1 GIT binary patch literal 4008 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 literal 0 HcmV?d00001 diff --git a/playground/sounds/zoop.mp3 b/playground/sounds/zoop.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f8af12d284a71e47a66adc487fdd984e2e06d42d GIT binary patch literal 1981 zcmajgc{J4f8wc?3Fbo>okVfH-nk&nU%pfC_t;vWJ*@v=5LY5Xwml;b8k&%5;iK~lI zvfT+|xeT(?CAYD}9b1twH>vRb>HL13jOs+G+CVxnaHG!-Rc@W1Bq@1IMf zndQ9|T_DecBVqwZH2%bB1@gv&o4*BZY|b6k^JAa@o)(g<*e{cqcBb{O_X=6WU-*K! zcoV2l9Gtysr`=bAv?2dpOhW&%kNS79QS9v#v_^61J6JSa(Do0D>|V5uinw6Q^WV17 zBQqV*+)qzxG{S=0M-VPeGK!e{^WZ2mzdEq1-~W7x`ck$Pl0w;pKD%y!JgdzY>GuPI}?+5 zE&Ezsvv)uqcQm-t-KbY>u1>oiQIf~P{`OS$!#1}mEH}b0Skvz8@|+{cWuB{8pX1is z&6t(n8y_vbGJ~Di8W~BdTOtpTtjpCHWPeBUq~)gfxK376?WhBnSc?7qZS+u8dW!6y zai#gE3!xy-h9hHuk!U#eCIWKMq*BUMn3Rxq8!8azi8fYq_5frEfB->zRaHgR1J$eI z@;nSm%~3JOggz2LCel0`NWC*g8r@zG$7_Wq(S(nR7w_6@a@{$#Io8#AJMd%-lHFsuyu^y zH0$)T(6rMz$9cg~If|bGOrxFko~}ty9LrE5i z7TZ?%_75a%90^vnomV}M&vFB`iUY?V^FzrJFN$>2IP2?~O4Z_X!pDbSwk4Z+9!t}) z*Rr|ZHOoJeau#ByC<8A&iQ6>4j`Z?eyB*(&Mj)vcUdoW9cwt!~q}4$Wx++qbdY$4I zJfSDq0TBaG#_epq*HL4Ujhx=bJ`Ej;Z$aK#l-1jct!)o=-S z1=a9>!e)Gq-ujlZ!~JX%w9T3gUa1irl*b@bBNNm&G|mMvDv|=#YF#N`q->|{O&&{? z0v_KNCv)0fVgPg=0_J4VT--=9iTL3Bcs>zB2;KhGv8_k0ss@E&EiQ@)hTL+#?-lIi zas_5WkJ}d~4KWQP(i0WMWC0xoXa7VAWd$MOKxn=WU^`C}O*cHCZWGw82vZhI(LnfQ z#^xN-Bf90YsJifBU0n#r6Yd-@fvP_np()i##~+RtR4q%8^G?buoG#QVQNyN`cwui$ zQarf%aUswYi^>x**l4%6dpBpD~Iwd+#O83(ABsdu1Ep-Q7dlkmNt^7uXELnZ)E+adC^y0lCz^O_Am*xN*c7&pMhu^RN%(g>cj^ zU~ZOE?hm1j6T0KAvR1n-Qef>$kgiT-q{vZ2gH_pXrltRwShBR76be@aGtL97*a0Se z0)DYFqIh21Oe${OCKRo-A4qE*@|9A)BjO230ptkg1ObTT`D(Spchre`L5~OV?_3|g z$jqj_sLCZhs?x*KS_h$VN@0u!2RwJ^MwH)Z!F-Qb`s2lmRzp5HO z(|LU;=W-m&@^9V!m-b`%yP7kJ& z+1E}VJ#f_DUtK>0I_Pz?VB_h@!Lma|D{%!8Uj5{MBiJg8f6BSbx~QQDOwZUC;;}={r)#xLc$5)ZsD-5#&>(!7TL*6P(zo$|F-lfUNC0A7=s`dN3Q)~+u^Au_#gRQE6Hau` z3I;3$X4t~Ff$5XN7KL(^ZGnW2cuH Date: Tue, 16 Aug 2016 12:04:24 -0400 Subject: [PATCH 003/721] fixed panner --- src/worker.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/worker.js b/src/worker.js index 1a92532d..5a7caac8 100644 --- a/src/worker.js +++ b/src/worker.js @@ -25,7 +25,7 @@ function VirtualMachine () { var pitchShift = new Tone.PitchShift(); - var panner = new Tone.Panner(0); + var panner = new Tone.Panner(); var reverb = new Tone.Freeverb(); reverb.wet.value = 0; @@ -144,7 +144,7 @@ function VirtualMachine () { case 'ECHO': delay.wet.value = (e.data.value / 100) / 2; // max 50% wet (need dry signal too) break; - case 'PANNER': + case 'PAN': panner.pan.value = e.data.value / 100; break; case 'REVERB': @@ -161,7 +161,7 @@ function VirtualMachine () { delay.wet.value += (e.data.value / 100) / 2; // max 50% wet (need dry signal too) delay.wet.value = clamp(delay.wet.value, 0, 0.5); break; - case 'PANNER': + case 'PAN': panner.pan.value += e.data.value / 100; panner.pan.value = clamp(panner.pan.value, -1, 1); break; From d5e46535e8be824cdf43959e05046414eb86063b Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 16 Aug 2016 16:32:35 -0400 Subject: [PATCH 004/721] playing with quantization --- playground/sounds/this_is_a_test.mp3 | Bin 0 -> 32960 bytes playground/sounds/who_put_the_bomp.mp3 | Bin 0 -> 89498 bytes src/blocks/scratch3_sound.js | 4 ++-- src/worker.js | 15 ++++++++++----- 4 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 playground/sounds/this_is_a_test.mp3 create mode 100644 playground/sounds/who_put_the_bomp.mp3 diff --git a/playground/sounds/this_is_a_test.mp3 b/playground/sounds/this_is_a_test.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..ed961d3ddc955db142b99eed259c661221ecb23c GIT binary patch literal 32960 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

p{z&fDf7OUde1p_GpHxK;_gh3d39N5^x*V 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 literal 0 HcmV?d00001 diff --git a/playground/sounds/who_put_the_bomp.mp3 b/playground/sounds/who_put_the_bomp.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..923a0fd78aab07b7525f9b46e7d0c4e45cbbdeac GIT binary patch literal 89498 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 vO_#qH zEqO33ootN^&CLv{mH+Q1n#cGnrGs-H3^E3U5|sg`A0l1};3L?ju|ehV|Nblg@Bi_C E04yEK4FCWD literal 0 HcmV?d00001 diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 300b02fd..5f5e4b5d 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -48,7 +48,7 @@ Scratch3SoundBlocks.prototype.playNoteForBeats = function (args, util) { return new Promise(function(resolve) { setTimeout(function() { resolve(); - }, 1000 * args.BEATS); + }, (1000 * args.BEATS) ); }); }; @@ -61,7 +61,7 @@ Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) { return new Promise(function(resolve) { setTimeout(function() { resolve(); - }, 1000 * args.BEATS); + }, (1000 * args.BEATS) ); }); }; diff --git a/src/worker.js b/src/worker.js index 5a7caac8..8514a72f 100644 --- a/src/worker.js +++ b/src/worker.js @@ -18,6 +18,11 @@ function VirtualMachine () { var tone = new Tone(); + Tone.Transport.start(); + + var quantizeUnit = '@8n'; + // var quantizeUnit = ''; + // effects var delay = new Tone.FeedbackDelay(0.25, 0.5); @@ -43,7 +48,7 @@ function VirtualMachine () { // sounds - var soundFileNames = ['meow', 'boing', 'cave', 'drip_drop', 'drum_machine', 'eggs', 'zoop']; + var soundFileNames = ['meow','boing','this_is_a_test','who_put_the_bomp','cave','drip_drop','drum_machine','eggs','zoop']; var soundSamplers = loadSoundFiles(soundFileNames); // polyphonic samplers @@ -112,17 +117,17 @@ function VirtualMachine () { function playNoteForBeats(note, beats) { var midiNote = scaleNoteToMidiNote(note, currentScale, rootNote); var freq = midiToFreq(midiNote); - synth.triggerAttackRelease(freq, beats); + synth.triggerAttackRelease(freq, beats, quantizeUnit); } // onmessage calls are converted into emitted events. instance.vmWorker.onmessage = function (e) { switch (e.data.method) { case 'playsound': - soundSamplers[e.data.soundnum].nextVoice().triggerAttack(); + soundSamplers[e.data.soundnum].nextVoice().triggerAttack(0, quantizeUnit); break; case 'playsoundwithpitch': - soundSamplers[e.data.soundnum].nextVoice().triggerAttack(e.data.pitch); + soundSamplers[e.data.soundnum].nextVoice().triggerAttack(e.data.pitch, quantizeUnit); break; case 'playnoteforbeats': playNoteForBeats(e.data.note, e.data.beats); @@ -137,7 +142,7 @@ function VirtualMachine () { case 'playdrumforbeats': case 'playdrum': var drumNum = e.data.drum - 1; // one-indexing - drumSamplers[drumNum].nextVoice().triggerAttack(); + drumSamplers[drumNum].nextVoice().triggerAttack(0, quantizeUnit); break; case 'seteffect' : switch (e.data.effect) { From fee8f61079fc2b23ddb59f9692fd15a94ad9addb Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 17 Aug 2016 11:38:01 -0400 Subject: [PATCH 005/721] removing attempted quantization --- src/worker.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/worker.js b/src/worker.js index 30186212..72753593 100644 --- a/src/worker.js +++ b/src/worker.js @@ -20,8 +20,8 @@ function VirtualMachine () { Tone.Transport.start(); - var quantizeUnit = '@8n'; - // var quantizeUnit = ''; + // var quantizeUnit = '@8n'; + var quantizeUnit = ''; // effects From 911a8fa02e7351d1eccd0160ad4baa82a1c8e229 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 17 Aug 2016 11:45:58 -0400 Subject: [PATCH 006/721] fixing quantize unit (oops) --- src/worker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/worker.js b/src/worker.js index 72753593..8f44d0b5 100644 --- a/src/worker.js +++ b/src/worker.js @@ -21,7 +21,7 @@ function VirtualMachine () { Tone.Transport.start(); // var quantizeUnit = '@8n'; - var quantizeUnit = ''; + var quantizeUnit = '0'; // effects From f8e460f1b42787b2baaeb0a9fccb777db3b26e88 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 17 Aug 2016 12:45:06 -0400 Subject: [PATCH 007/721] fixing quantize unit again --- src/worker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/worker.js b/src/worker.js index 8f44d0b5..bb054d60 100644 --- a/src/worker.js +++ b/src/worker.js @@ -21,7 +21,7 @@ function VirtualMachine () { Tone.Transport.start(); // var quantizeUnit = '@8n'; - var quantizeUnit = '0'; + var quantizeUnit = '+0'; // effects From 5e268d57d354059879d07de2a34082dfb33d8450 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 17 Aug 2016 16:45:01 -0400 Subject: [PATCH 008/721] scale to midi note reporter --- playground/index.html | 15 +++++++++++++++ src/blocks/scratch3_sound.js | 26 ++++++++++++++++++++++++++ src/worker.js | 5 +++-- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/playground/index.html b/playground/index.html index 9782a7f6..d233b103 100644 --- a/playground/index.html +++ b/playground/index.html @@ -325,6 +325,21 @@ + + + + 1 + + + + + + + + + + + diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 5f5e4b5d..ecd32676 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -17,6 +17,7 @@ Scratch3SoundBlocks.prototype.getPrimitives = function() { 'sound_stopallsounds': this.stopAllSounds, 'sound_playnote': this.playNote, 'sound_playnoteforbeats': this.playNoteForBeats, + 'sound_scalenotetomidinote': this.scaleNoteToMidiNote, 'sound_playdrum': this.playDrum, 'sound_playdrumforbeats': this.playDrumForBeats, 'sound_setkey' : this.setKey, @@ -56,6 +57,31 @@ Scratch3SoundBlocks.prototype.playNote = function (args, util) { self.postMessage({method: 'playnote', note:args.NOTE}); }; +Scratch3SoundBlocks.prototype.scaleNoteToMidiNote = function (args, util) { + + var root = parseInt(args.ROOT) + 60; + + var scales = { + 'MAJOR' : [0,2,4,5,7,9,11], + 'MINOR' : [0,2,3,5,7,8,10], + 'PENTATONIC': [0, 2, 4, 7, 9], + 'CHROMATIC' : [0,1,2,3,4,5,6,7,8,9,10,11], + }; + + var scale = scales[args.SCALE]; + + var scaleNote = args.NOTE; + + var scaleIndex = (Math.round(scaleNote) - 1) % scale.length; + if (scaleIndex < 0) { + scaleIndex += scale.length; + } + var octave = Math.floor((scaleNote - 1) / scale.length); + var midiNote = root + (octave * 12) + scale[scaleIndex]; + + return midiNote; +}; + Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) { self.postMessage({method: 'playdrumforbeats', drum:args.DRUMTYPE, beats:args.BEATS}); return new Promise(function(resolve) { diff --git a/src/worker.js b/src/worker.js index bb054d60..b427fd61 100644 --- a/src/worker.js +++ b/src/worker.js @@ -115,8 +115,9 @@ function VirtualMachine () { }; function playNoteForBeats(note, beats) { - var midiNote = scaleNoteToMidiNote(note, currentScale, rootNote); - var freq = midiToFreq(midiNote); + // var midiNote = scaleNoteToMidiNote(note, currentScale, rootNote); + + var freq = midiToFreq(note); synth.triggerAttackRelease(freq, beats, quantizeUnit); } From 8bf2949eead9258e9bc174357d273c434be53c2f Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 6 Sep 2016 12:54:58 -0400 Subject: [PATCH 009/721] play note block uses scale degree input --- src/worker.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/worker.js b/src/worker.js index b427fd61..fccfa181 100644 --- a/src/worker.js +++ b/src/worker.js @@ -115,9 +115,10 @@ function VirtualMachine () { }; function playNoteForBeats(note, beats) { - // var midiNote = scaleNoteToMidiNote(note, currentScale, rootNote); +// var freq = midiToFreq(note); - var freq = midiToFreq(note); + var midiNote = scaleNoteToMidiNote(note, currentScale, rootNote); + var freq = midiToFreq(midiNote); synth.triggerAttackRelease(freq, beats, quantizeUnit); } From 7709a4095eae68b7667171cf4a029cf973089a32 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 7 Sep 2016 16:55:38 -0400 Subject: [PATCH 010/721] first working audio engine using audioworker --- Tone.min.js | 13 +++++ audioLocal.js | 94 ++++++++++++++++++++++++++++++++++++ audioWorker.js | 13 +++++ playground/index.html | 3 ++ playground/playground.js | 4 ++ src/blocks/scratch3_sound.js | 3 +- src/index.js | 3 ++ src/sprites/clone.js | 9 ++++ src/worker.js | 9 ++-- 9 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 Tone.min.js create mode 100644 audioLocal.js create mode 100644 audioWorker.js diff --git a/Tone.min.js b/Tone.min.js new file mode 100644 index 00000000..c0e5d61e --- /dev/null +++ b/Tone.min.js @@ -0,0 +1,13 @@ +!function(t,e){"function"==typeof define&&define.amd?define(function(){return e()}):"object"==typeof module?module.exports=e():t.Tone=e()}(this,function(){"use strict";function t(t){i=t()}function e(t){t(i)}var i;/** + * Tone.js + * @author Yotam Mann + * @license http://opensource.org/licenses/MIT MIT License + * @copyright 2014-2016 Yotam Mann + */ +return t(function(){function t(t){return void 0===t}function e(t){return"function"==typeof t}var i,s,n,o;if(t(window.AudioContext)&&(window.AudioContext=window.webkitAudioContext),t(window.OfflineAudioContext)&&(window.OfflineAudioContext=window.webkitOfflineAudioContext),t(AudioContext))throw new Error("Web Audio is not supported in this browser");return i=new AudioContext,e(AudioContext.prototype.createGain)||(AudioContext.prototype.createGain=AudioContext.prototype.createGainNode),e(AudioContext.prototype.createDelay)||(AudioContext.prototype.createDelay=AudioContext.prototype.createDelayNode),e(AudioContext.prototype.createPeriodicWave)||(AudioContext.prototype.createPeriodicWave=AudioContext.prototype.createWaveTable),e(AudioBufferSourceNode.prototype.start)||(AudioBufferSourceNode.prototype.start=AudioBufferSourceNode.prototype.noteGrainOn),e(AudioBufferSourceNode.prototype.stop)||(AudioBufferSourceNode.prototype.stop=AudioBufferSourceNode.prototype.noteOff),e(OscillatorNode.prototype.start)||(OscillatorNode.prototype.start=OscillatorNode.prototype.noteOn),e(OscillatorNode.prototype.stop)||(OscillatorNode.prototype.stop=OscillatorNode.prototype.noteOff),e(OscillatorNode.prototype.setPeriodicWave)||(OscillatorNode.prototype.setPeriodicWave=OscillatorNode.prototype.setWaveTable),t(AudioNode.prototype._nativeConnect)&&(AudioNode.prototype._nativeConnect=AudioNode.prototype.connect,AudioNode.prototype.connect=function(e,i,s){if(e.input)Array.isArray(e.input)?(t(s)&&(s=0),this.connect(e.input[s])):this.connect(e.input,i,s);else try{e instanceof AudioNode?this._nativeConnect(e,i,s):this._nativeConnect(e,i)}catch(n){throw new Error("error connecting to node: "+e)}}),s=function(e,i){t(e)||1===e?this.input=this.context.createGain():e>1&&(this.input=new Array(e)),t(i)||1===i?this.output=this.context.createGain():i>1&&(this.output=new Array(e))},s.prototype.set=function(e,i,n){var o,r,a,h,l,u;this.isObject(e)?n=i:this.isString(e)&&(o={},o[e]=i,e=o);for(r in e){if(i=e[r],a=this,-1!==r.indexOf(".")){for(h=r.split("."),l=0;l1)for(t=arguments[0],e=1;e0)for(t=this,e=0;e0)for(var t=0;te;e++)s=e/(i-1)*2-1,this._curve[e]=t(s,e);return this._shaper.curve=this._curve,this},Object.defineProperty(t.WaveShaper.prototype,"curve",{get:function(){return this._shaper.curve},set:function(t){this._curve=new Float32Array(t),this._shaper.curve=this._curve}}),Object.defineProperty(t.WaveShaper.prototype,"oversample",{get:function(){return this._shaper.oversample},set:function(t){if(-1===["none","2x","4x"].indexOf(t))throw new RangeError("Tone.WaveShaper: oversampling must be either 'none', '2x', or '4x'");this._shaper.oversample=t}}),t.WaveShaper.prototype.dispose=function(){return t.prototype.dispose.call(this),this._shaper.disconnect(),this._shaper=null,this._curve=null,this},t.WaveShaper}),e(function(t){return t.TimeBase=function(e,i){if(!(this instanceof t.TimeBase))return new t.TimeBase(e,i);if(this._expr=this._noOp,i=this.defaultArg(i,this._defaultUnits),this.isString(e))this._expr=this._parseExprString(e);else if(this.isNumber(e)){var s=this._primaryExpressions[i].method;this._expr=s.bind(this,e)}else this.isUndef(e)?this._expr=this._defaultExpr():e instanceof t.TimeBase&&(this._expr=e._expr)},t.extend(t.TimeBase),t.TimeBase.prototype.set=function(t){return this._expr=this._parseExprString(t),this},t.TimeBase.prototype._primaryExpressions={n:{regexp:/^(\d+)n/i,method:function(t){return t=parseInt(t),1===t?this._beatsToUnits(this._timeSignature()):this._beatsToUnits(4/t)}},t:{regexp:/^(\d+)t/i,method:function(t){return t=parseInt(t),this._beatsToUnits(8/(3*parseInt(t)))}},m:{regexp:/^(\d+)m/i,method:function(t){return this._beatsToUnits(parseInt(t)*this._timeSignature())}},i:{regexp:/^(\d+)i/i,method:function(t){return this._ticksToUnits(parseInt(t))}},hz:{regexp:/^(\d+(?:\.\d+)?)hz/i,method:function(t){return this._frequencyToUnits(parseFloat(t))}},tr:{regexp:/^(\d+(?:\.\d+)?):(\d+(?:\.\d+)?):?(\d+(?:\.\d+)?)?/,method:function(t,e,i){var s=0;return t&&"0"!==t&&(s+=this._beatsToUnits(this._timeSignature()*parseFloat(t))),e&&"0"!==e&&(s+=this._beatsToUnits(parseFloat(e))),i&&"0"!==i&&(s+=this._beatsToUnits(parseFloat(i)/4)),s}},s:{regexp:/^(\d+(?:\.\d+)?s)/,method:function(t){return this._secondsToUnits(parseFloat(t))}},samples:{regexp:/^(\d+)samples/,method:function(t){return parseInt(t)/this.context.sampleRate}},"default":{regexp:/^(\d+(?:\.\d+)?)/,method:function(t){return this._primaryExpressions[this._defaultUnits].method.call(this,t)}}},t.TimeBase.prototype._binaryExpressions={"+":{regexp:/^\+/,precedence:2,method:function(t,e){return t()+e()}},"-":{regexp:/^\-/,precedence:2,method:function(t,e){return t()-e()}},"*":{regexp:/^\*/,precedence:1,method:function(t,e){return t()*e()}},"/":{regexp:/^\//,precedence:1,method:function(t,e){return t()/e()}}},t.TimeBase.prototype._unaryExpressions={neg:{regexp:/^\-/,method:function(t){return-t()}}},t.TimeBase.prototype._syntaxGlue={"(":{regexp:/^\(/},")":{regexp:/^\)/}},t.TimeBase.prototype._tokenize=function(t){function e(t,e){var i,s,n,o,r,a,h=["_binaryExpressions","_unaryExpressions","_primaryExpressions","_syntaxGlue"];for(i=0;i0;)t=t.trim(),i=e(t,this),n.push(i),t=t.substr(i.value.length);return{next:function(){return n[++s]},peek:function(){return n[s+1]}}},t.TimeBase.prototype._matchGroup=function(t,e,i){var s,n,o=!1;if(!this.isUndef(t))for(s in e)if(n=e[s],n.regexp.test(t.value)){if(this.isUndef(i))return n;if(n.precedence===i)return n}return o},t.TimeBase.prototype._parseBinary=function(t,e){var i,s;for(this.isUndef(e)&&(e=2),i=0>e?this._parseUnary(t):this._parseBinary(t,e-1),s=t.peek();s&&this._matchGroup(s,this._binaryExpressions,e);)s=t.next(),i=s.method.bind(this,i,this._parseBinary(t,e-1)),s=t.peek();return i},t.TimeBase.prototype._parseUnary=function(t){var e,i=t.peek(),s=this._matchGroup(i,this._unaryExpressions);return s?(i=t.next(),e=this._parseUnary(t),s.method.bind(this,e)):this._parsePrimary(t)},t.TimeBase.prototype._parsePrimary=function(t){var e,i,s=t.peek();if(this.isUndef(s))throw new SyntaxError("Tone.TimeBase: Unexpected end of expression");if(this._matchGroup(s,this._primaryExpressions))return s=t.next(),i=s.value.match(s.regexp),s.method.bind(this,i[1],i[2],i[3]);if(s&&"("===s.value){if(t.next(),e=this._parseBinary(t),s=t.next(),!s||")"!==s.value)throw new SyntaxError("Expected )");return e}throw new SyntaxError("Tone.TimeBase: Cannot process token "+s.value)},t.TimeBase.prototype._parseExprString=function(t){var e=this._tokenize(t),i=this._parseBinary(e);return i},t.TimeBase.prototype._noOp=function(){return 0},t.TimeBase.prototype._defaultExpr=function(){return this._noOp},t.TimeBase.prototype._defaultUnits="s",t.TimeBase.prototype._frequencyToUnits=function(t){return 1/t},t.TimeBase.prototype._beatsToUnits=function(e){return 60/t.Transport.bpm.value*e},t.TimeBase.prototype._secondsToUnits=function(t){return t},t.TimeBase.prototype._ticksToUnits=function(e){return e*(this._beatsToUnits(1)/t.Transport.PPQ)},t.TimeBase.prototype._timeSignature=function(){return t.Transport.timeSignature},t.TimeBase.prototype._pushExpr=function(e,i,s){return e instanceof t.TimeBase||(e=new t.TimeBase(e,s)),this._expr=this._binaryExpressions[i].method.bind(this,this._expr,e._expr),this},t.TimeBase.prototype.add=function(t,e){return this._pushExpr(t,"+",e)},t.TimeBase.prototype.sub=function(t,e){return this._pushExpr(t,"-",e)},t.TimeBase.prototype.mult=function(t,e){return this._pushExpr(t,"*",e)},t.TimeBase.prototype.div=function(t,e){return this._pushExpr(t,"/",e)},t.TimeBase.prototype.eval=function(){return this._expr()},t.TimeBase.prototype.dispose=function(){this._expr=null},t.TimeBase}),e(function(t){return t.Time=function(e,i){return this instanceof t.Time?(this._plusNow=!1,void t.TimeBase.call(this,e,i)):new t.Time(e,i)},t.extend(t.Time,t.TimeBase),t.Time.prototype._unaryExpressions=Object.create(t.TimeBase.prototype._unaryExpressions),t.Time.prototype._unaryExpressions.quantize={regexp:/^@/,method:function(e){return t.Transport.nextSubdivision(e())}},t.Time.prototype._unaryExpressions.now={regexp:/^\+/,method:function(t){return this._plusNow=!0,t()}},t.Time.prototype.quantize=function(t,e){return e=this.defaultArg(e,1),this._expr=function(t,e,i){var s,n,o;return t=t(),e=e.toSeconds(),s=Math.round(t/e),n=s*e,o=n-t,t+o*i}.bind(this,this._expr,new this.constructor(t),e),this},t.Time.prototype.addNow=function(){return this._plusNow=!0,this},t.Time.prototype._defaultExpr=function(){return this._plusNow=!0,this._noOp},t.Time.prototype.toNotation=function(){var t=this.toSeconds(),e=["1m","2n","4n","8n","16n","32n","64n","128n"],i=this._toNotationHelper(t,e),s=["1m","2n","2t","4n","4t","8n","8t","16n","16t","32n","32t","64n","64t","128n"],n=this._toNotationHelper(t,s);return n.split("+").length1-n%1&&(n+=o),n=Math.floor(n),n>0){if(a+=1===n?e[i]:n.toString()+"*"+e[i],t-=n*s,r>t)break;a+=" + "}return""===a&&(a="0"),a},t.Time.prototype._notationToUnits=function(t){var e,i,s,n=this._primaryExpressions,o=[n.n,n.t,n.m];for(e=0;e3&&(n=parseFloat(n).toFixed(3)),t=[s,i,n],t.join(":")},t.Time.prototype.toTicks=function(){var e=this._beatsToUnits(1),i=this.eval()/e;return Math.floor(i*t.Transport.PPQ)},t.Time.prototype.toSamples=function(){return this.toSeconds()*this.context.sampleRate},t.Time.prototype.toFrequency=function(){return 1/this.eval()},t.Time.prototype.toSeconds=function(){return this._expr()},t.Time.prototype.eval=function(){var t=this._expr();return t+(this._plusNow?this.now():0)},t.Time}),e(function(t){var e,i;return t.Frequency=function(e,i){return this instanceof t.Frequency?void t.TimeBase.call(this,e,i):new t.Frequency(e,i)},t.extend(t.Frequency,t.TimeBase),t.Frequency.prototype._primaryExpressions=Object.create(t.TimeBase.prototype._primaryExpressions),t.Frequency.prototype._primaryExpressions.midi={regexp:/^(\d+(?:\.\d+)?midi)/,method:function(t){return this.midiToFrequency(t)}},t.Frequency.prototype._primaryExpressions.note={regexp:/^([a-g]{1}(?:b|#|x|bb)?)(-?[0-9]+)/i,method:function(t,i){var s=e[t.toLowerCase()],n=s+12*(parseInt(i)+1);return this.midiToFrequency(n)}},t.Frequency.prototype._primaryExpressions.tr={regexp:/^(\d+(?:\.\d+)?):(\d+(?:\.\d+)?):?(\d+(?:\.\d+)?)?/,method:function(t,e,i){var s=1;return t&&"0"!==t&&(s*=this._beatsToUnits(this._timeSignature()*parseFloat(t))),e&&"0"!==e&&(s*=this._beatsToUnits(parseFloat(e))),i&&"0"!==i&&(s*=this._beatsToUnits(parseFloat(i)/4)),s}},t.Frequency.prototype.transpose=function(t){return this._expr=function(t,e){var i=t();return i*this.intervalToFrequencyRatio(e)}.bind(this,this._expr,t),this},t.Frequency.prototype.harmonize=function(t){return this._expr=function(t,e){var i,s=t(),n=[];for(i=0;ir&&(o+=-12*r),e=i[o%12],e+r.toString()},t.Frequency.prototype.toSeconds=function(){return 1/this.eval()},t.Frequency.prototype.toTicks=function(){var e=this._beatsToUnits(1),i=this.eval()/e;return Math.floor(i*t.Transport.PPQ)},t.Frequency.prototype._frequencyToUnits=function(t){return t},t.Frequency.prototype._ticksToUnits=function(e){return 1/(60*e/(t.Transport.bpm.value*t.Transport.PPQ))},t.Frequency.prototype._beatsToUnits=function(e){return 1/t.TimeBase.prototype._beatsToUnits.call(this,e)},t.Frequency.prototype._secondsToUnits=function(t){return 1/t},t.Frequency.prototype._defaultUnits="hz",e={cbb:-2,cb:-1,c:0,"c#":1,cx:2,dbb:0,db:1,d:2,"d#":3,dx:4,ebb:2,eb:3,e:4,"e#":5,ex:6,fbb:3,fb:4,f:5,"f#":6,fx:7,gbb:5,gb:6,g:7,"g#":8,gx:9,abb:7,ab:8,a:9,"a#":10,ax:11,bbb:9,bb:10,b:11,"b#":12,bx:13},i=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"],t.Frequency.A4=440,t.Frequency.prototype.midiToFrequency=function(e){return t.Frequency.A4*Math.pow(2,(e-69)/12)},t.Frequency.prototype.frequencyToMidi=function(e){return 69+12*Math.log(e/t.Frequency.A4)/Math.LN2},t.Frequency}),e(function(t){return t.TransportTime=function(e,i){return this instanceof t.TransportTime?void t.Time.call(this,e,i):new t.TransportTime(e,i)},t.extend(t.TransportTime,t.Time),t.TransportTime.prototype._unaryExpressions=Object.create(t.Time.prototype._unaryExpressions),t.TransportTime.prototype._unaryExpressions.quantize={regexp:/^@/,method:function(e){var i=this._secondsToTicks(e()),s=Math.ceil(t.Transport.ticks/i);return this._ticksToUnits(s*i)}},t.TransportTime.prototype._secondsToTicks=function(e){var i=this._beatsToUnits(1),s=e/i;return Math.round(s*t.Transport.PPQ)},t.TransportTime.prototype.eval=function(){var e=this._secondsToTicks(this._expr());return e+(this._plusNow?t.Transport.ticks:0)},t.TransportTime.prototype.toTicks=function(){return this.eval()},t.TransportTime.prototype.toFrequency=function(){return 1/this.toSeconds()},t.TransportTime}),e(function(t){return t.Type={Default:"number",Time:"time",Frequency:"frequency",TransportTime:"transportTime",Ticks:"ticks",NormalRange:"normalRange",AudioRange:"audioRange",Decibels:"db",Interval:"interval",BPM:"bpm",Positive:"positive",Cents:"cents",Degrees:"degrees",MIDI:"midi",BarsBeatsSixteenths:"barsBeatsSixteenths",Samples:"samples",Hertz:"hertz",Note:"note",Milliseconds:"milliseconds",Seconds:"seconds",Notation:"notation"},t.prototype.toSeconds=function(e){return this.isNumber(e)?e:this.isString(e)||this.isUndef(e)?new t.Time(e).eval():e instanceof t.TransportTime?e.toSeconds():e instanceof t.Time?e.eval():e instanceof t.Frequency?e.toSeconds():void 0},t.prototype.toFrequency=function(e){return this.isNumber(e)?e:this.isString(e)||this.isUndef(e)?new t.Frequency(e).eval():e instanceof t.Frequency?e.eval():e instanceof t.Time?e.toFrequency():void 0},t.prototype.toTicks=function(e){return this.isNumber(e)||this.isString(e)||this.isUndef(e)?new t.TransportTime(e).eval():e instanceof t.Frequency?e.toTicks():e instanceof t.TransportTime?e.eval():e instanceof t.Time?e.toTicks():void 0},t}),e(function(t){return t.Param=function(){var e=this.optionsObject(arguments,["param","units","convert"],t.Param.defaults);this._param=this.input=e.param,this.units=e.units,this.convert=e.convert,this.overridden=!1,this.isUndef(e.value)||(this.value=e.value)},t.extend(t.Param),t.Param.defaults={units:t.Type.Default,convert:!0,param:void 0},Object.defineProperty(t.Param.prototype,"value",{get:function(){return this._toUnits(this._param.value)},set:function(t){var e=this._fromUnits(t);this._param.cancelScheduledValues(0),this._param.value=e}}),t.Param.prototype._fromUnits=function(e){if(!this.convert&&!this.isUndef(this.convert))return e;switch(this.units){case t.Type.Time:return this.toSeconds(e);case t.Type.Frequency:return this.toFrequency(e);case t.Type.Decibels:return this.dbToGain(e);case t.Type.NormalRange:return Math.min(Math.max(e,0),1);case t.Type.AudioRange:return Math.min(Math.max(e,-1),1);case t.Type.Positive:return Math.max(e,0);default:return e}},t.Param.prototype._toUnits=function(e){if(!this.convert&&!this.isUndef(this.convert))return e;switch(this.units){case t.Type.Decibels:return this.gainToDb(e);default:return e}},t.Param.prototype._minOutput=1e-5,t.Param.prototype.setValueAtTime=function(t,e){return t=this._fromUnits(t),this._param.setValueAtTime(t,this.toSeconds(e)),this},t.Param.prototype.setRampPoint=function(t){t=this.defaultArg(t,this.now());var e=this._param.value;return 0===e&&(e=this._minOutput),this._param.setValueAtTime(e,t),this},t.Param.prototype.linearRampToValueAtTime=function(t,e){return t=this._fromUnits(t),this._param.linearRampToValueAtTime(t,this.toSeconds(e)),this},t.Param.prototype.exponentialRampToValueAtTime=function(t,e){return t=this._fromUnits(t),t=Math.max(this._minOutput,t),this._param.exponentialRampToValueAtTime(t,this.toSeconds(e)),this},t.Param.prototype.exponentialRampToValue=function(t,e,i){return i=this.toSeconds(i),this.setRampPoint(i),this.exponentialRampToValueAtTime(t,i+this.toSeconds(e)),this},t.Param.prototype.linearRampToValue=function(t,e,i){return i=this.toSeconds(i),this.setRampPoint(i),this.linearRampToValueAtTime(t,i+this.toSeconds(e)),this},t.Param.prototype.setTargetAtTime=function(t,e,i){return t=this._fromUnits(t),t=Math.max(this._minOutput,t),i=Math.max(this._minOutput,i),this._param.setTargetAtTime(t,this.toSeconds(e),i),this},t.Param.prototype.setValueCurveAtTime=function(t,e,i){for(var s=0;sthis.memory&&(i=this.length-this.memory,this._timeline.splice(0,i)),this},t.Timeline.prototype.removeEvent=function(t){if(this._iterating)this._toRemove.push(t);else{var e=this._timeline.indexOf(t);-1!==e&&this._timeline.splice(e,1)}return this},t.Timeline.prototype.getEvent=function(t){t=this.toSeconds(t);var e=this._search(t);return-1!==e?this._timeline[e]:null},t.Timeline.prototype.getEventAfter=function(t){t=this.toSeconds(t);var e=this._search(t);return e+10&&this._timeline[e-1].time=0?this._timeline[i-1]:null)},t.Timeline.prototype.cancel=function(t){var e,i;if(this._timeline.length>1)if(t=this.toSeconds(t),e=this._search(t),e>=0)if(this._timeline[e].time===t){for(i=e;i>=0&&this._timeline[i].time===t;i--)e=i;this._timeline=this._timeline.slice(0,e)}else this._timeline=this._timeline.slice(0,e+1);else this._timeline=[];else 1===this._timeline.length&&this._timeline[0].time>=t&&(this._timeline=[]);return this},t.Timeline.prototype.cancelBefore=function(t){if(this._timeline.length){t=this.toSeconds(t);var e=this._search(t);e>=0&&(this._timeline=this._timeline.slice(e+1))}return this},t.Timeline.prototype._search=function(t){var e,i,s,n,o,r=0,a=this._timeline.length,h=a;if(a>0&&this._timeline[a-1].time<=t)return a-1;for(;h>r;){if(e=Math.floor(r+(h-r)/2),i=this._timeline[e],s=this._timeline[e+1],i.time===t){for(n=e;nt)return e;i.time>t?h=e:i.time=s;s++)t(this._timeline[s]);if(this._iterating=!1,this._toRemove.length>0){for(n=0;n=0&&this._timeline[i].time>=t;)i--;return this._iterate(e,i+1),this},t.Timeline.prototype.forEachAtTime=function(t,e){t=this.toSeconds(t);var i=this._search(t);return-1!==i&&this._iterate(function(i){i.time===t&&e(i)},0,i),this},t.Timeline.prototype.dispose=function(){t.prototype.dispose.call(this),this._timeline=null,this._toRemove=null},t.Timeline}),e(function(t){return t.TimelineSignal=function(){var e=this.optionsObject(arguments,["value","units"],t.Signal.defaults);this._events=new t.Timeline(10),t.Signal.apply(this,e),e.param=this._param,t.Param.call(this,e),this._initial=this._fromUnits(this._param.value)},t.extend(t.TimelineSignal,t.Param),t.TimelineSignal.Type={Linear:"linear",Exponential:"exponential",Target:"target",Curve:"curve",Set:"set"},Object.defineProperty(t.TimelineSignal.prototype,"value",{get:function(){var t=this.now(),e=this.getValueAtTime(t);return this._toUnits(e)},set:function(t){var e=this._fromUnits(t);this._initial=e,this.cancelScheduledValues(),this._param.value=e}}),t.TimelineSignal.prototype.setValueAtTime=function(e,i){return e=this._fromUnits(e),i=this.toSeconds(i),this._events.addEvent({type:t.TimelineSignal.Type.Set,value:e,time:i}),this._param.setValueAtTime(e,i),this},t.TimelineSignal.prototype.linearRampToValueAtTime=function(e,i){return e=this._fromUnits(e),i=this.toSeconds(i),this._events.addEvent({type:t.TimelineSignal.Type.Linear,value:e,time:i}),this._param.linearRampToValueAtTime(e,i),this},t.TimelineSignal.prototype.exponentialRampToValueAtTime=function(e,i){var s,n=this._searchBefore(i);return n&&0===n.value&&this.setValueAtTime(this._minOutput,n.time),e=this._fromUnits(e),s=Math.max(e,this._minOutput),i=this.toSeconds(i),this._events.addEvent({type:t.TimelineSignal.Type.Exponential,value:s,time:i}),ee?(this.cancelScheduledValues(e),this.linearRampToValueAtTime(i,e)):(n=this._searchAfter(e),n&&(this.cancelScheduledValues(e),n.type===t.TimelineSignal.Type.Linear?this.linearRampToValueAtTime(i,e):n.type===t.TimelineSignal.Type.Exponential&&this.exponentialRampToValueAtTime(i,e)),this.setValueAtTime(i,e)),this},t.TimelineSignal.prototype.linearRampToValueBetween=function(t,e,i){return this.setRampPoint(e),this.linearRampToValueAtTime(t,i),this},t.TimelineSignal.prototype.exponentialRampToValueBetween=function(t,e,i){return this.setRampPoint(e),this.exponentialRampToValueAtTime(t,i),this},t.TimelineSignal.prototype._searchBefore=function(t){return this._events.getEvent(t)},t.TimelineSignal.prototype._searchAfter=function(t){return this._events.getEventAfter(t)},t.TimelineSignal.prototype.getValueAtTime=function(e){var i,s,n=this._searchAfter(e),o=this._searchBefore(e),r=this._initial;return null===o?r=this._initial:o.type===t.TimelineSignal.Type.Target?(i=this._events.getEventBefore(o.time), +s=null===i?this._initial:i.value,r=this._exponentialApproach(o.time,s,o.value,o.constant,e)):r=o.type===t.TimelineSignal.Type.Curve?this._curveInterpolate(o.time,o.value,o.duration,e):null===n?o.value:n.type===t.TimelineSignal.Type.Linear?this._linearInterpolate(o.time,o.value,n.time,n.value,e):n.type===t.TimelineSignal.Type.Exponential?this._exponentialInterpolate(o.time,o.value,n.time,n.value,e):o.value,r},t.TimelineSignal.prototype.connect=t.SignalBase.prototype.connect,t.TimelineSignal.prototype._exponentialApproach=function(t,e,i,s,n){return i+(e-i)*Math.exp(-(n-t)/s)},t.TimelineSignal.prototype._linearInterpolate=function(t,e,i,s,n){return e+(s-e)*((n-t)/(i-t))},t.TimelineSignal.prototype._exponentialInterpolate=function(t,e,i,s,n){return e=Math.max(this._minOutput,e),e*Math.pow(s/e,(n-t)/(i-t))},t.TimelineSignal.prototype._curveInterpolate=function(t,e,i,s){var n,o,r,a,h,l=e.length;return s>=t+i?e[l-1]:t>=s?e[0]:(n=(s-t)/i,o=Math.floor((l-1)*n),r=Math.ceil((l-1)*n),a=e[o],h=e[r],r===o?a:this._linearInterpolate(o,a,r,h,n*(l-1)))},t.TimelineSignal.prototype.dispose=function(){t.Signal.prototype.dispose.call(this),t.Param.prototype.dispose.call(this),this._events.dispose(),this._events=null},t.TimelineSignal}),e(function(t){return t.Pow=function(e){this._exp=this.defaultArg(e,1),this._expScaler=this.input=this.output=new t.WaveShaper(this._expFunc(this._exp),8192)},t.extend(t.Pow,t.SignalBase),Object.defineProperty(t.Pow.prototype,"value",{get:function(){return this._exp},set:function(t){this._exp=t,this._expScaler.setMap(this._expFunc(this._exp))}}),t.Pow.prototype._expFunc=function(t){return function(e){return Math.pow(Math.abs(e),t)}},t.Pow.prototype.dispose=function(){return t.prototype.dispose.call(this),this._expScaler.dispose(),this._expScaler=null,this},t.Pow}),e(function(t){return t.Envelope=function(){var e=this.optionsObject(arguments,["attack","decay","sustain","release"],t.Envelope.defaults);this.attack=e.attack,this.decay=e.decay,this.sustain=e.sustain,this.release=e.release,this._attackCurve="linear",this._releaseCurve="exponential",this._sig=this.output=new t.TimelineSignal,this._sig.setValueAtTime(0,0),this.attackCurve=e.attackCurve,this.releaseCurve=e.releaseCurve},t.extend(t.Envelope),t.Envelope.defaults={attack:.01,decay:.1,sustain:.5,release:1,attackCurve:"linear",releaseCurve:"exponential"},Object.defineProperty(t.Envelope.prototype,"value",{get:function(){return this.getValueAtTime(this.now())}}),Object.defineProperty(t.Envelope.prototype,"attackCurve",{get:function(){if(this.isString(this._attackCurve))return this._attackCurve;if(this.isArray(this._attackCurve)){for(var e in t.Envelope.Type)if(t.Envelope.Type[e].In===this._attackCurve)return e;return this._attackCurve}},set:function(e){if(t.Envelope.Type.hasOwnProperty(e)){var i=t.Envelope.Type[e];this.isObject(i)?this._attackCurve=i.In:this._attackCurve=i}else{if(!this.isArray(e))throw new Error("Tone.Envelope: invalid curve: "+e);this._attackCurve=e}}}),Object.defineProperty(t.Envelope.prototype,"releaseCurve",{get:function(){if(this.isString(this._releaseCurve))return this._releaseCurve;if(this.isArray(this._releaseCurve)){for(var e in t.Envelope.Type)if(t.Envelope.Type[e].Out===this._releaseCurve)return e;return this._releaseCurve}},set:function(e){if(t.Envelope.Type.hasOwnProperty(e)){var i=t.Envelope.Type[e];this.isObject(i)?this._releaseCurve=i.Out:this._releaseCurve=i}else{if(!this.isArray(e))throw new Error("Tone.Envelope: invalid curve: "+e);this._releaseCurve=e}}}),t.Envelope.prototype.triggerAttack=function(t,e){var i,s,n,o,r,a,h,l,u,c=this.now()+this.blockTime;return t=this.toSeconds(t,c),i=this.toSeconds(this.attack),s=i,n=this.toSeconds(this.decay),e=this.defaultArg(e,1),o=this.getValueAtTime(t),o>0&&(r=1/s,a=1-o,s=a/r),"linear"===this._attackCurve?this._sig.linearRampToValue(e,s,t):"exponential"===this._attackCurve?this._sig.exponentialRampToValue(e,s,t):s>0&&(this._sig.setRampPoint(t),h=this._attackCurve,i>s&&(l=1-s/i,u=Math.floor(l*this._attackCurve.length),h=this._attackCurve.slice(u),h[0]=o),this._sig.setValueCurveAtTime(h,t,s,e)),this._sig.exponentialRampToValue(e*this.sustain,n,s+t),this},t.Envelope.prototype.triggerRelease=function(t){var e,i,s,n=this.now()+this.blockTime;return t=this.toSeconds(t,n),e=this.getValueAtTime(t),e>0&&(i=this.toSeconds(this.release),"linear"===this._releaseCurve?this._sig.linearRampToValue(0,i,t):"exponential"===this._releaseCurve?this._sig.exponentialRampToValue(0,i,t):(s=this._releaseCurve,this.isArray(s)&&(this._sig.setRampPoint(t),this._sig.setValueCurveAtTime(s,t,i,e)))),this},t.Envelope.prototype.getValueAtTime=function(t){return this._sig.getValueAtTime(t)},t.Envelope.prototype.triggerAttackRelease=function(t,e,i){return e=this.toSeconds(e),this.triggerAttack(e,i),this.triggerRelease(e+this.toSeconds(t)),this},t.Envelope.prototype.cancel=function(t){return this._sig.cancelScheduledValues(t),this},t.Envelope.prototype.connect=t.Signal.prototype.connect,function(){function e(t){var e,i=new Array(t.length);for(e=0;es;s++)_[s]=Math.sin(s/(d-1)*(Math.PI/2));for(o=[],r=6.4,s=0;d-1>s;s++)n=s/(d-1),a=Math.sin(n*(2*Math.PI)*r-Math.PI/2)+1,o[s]=a/10+.83*n;for(o[d-1]=1,h=[],l=5,s=0;d>s;s++)h[s]=Math.ceil(s/(d-1)*l)/l;for(u=[],s=0;d>s;s++)n=s/(d-1),u[s]=.5*(1-Math.cos(Math.PI*n));for(c=[],s=0;d>s;s++)n=s/(d-1),p=4*Math.pow(n,3)+.2,f=Math.cos(p*Math.PI*2*n),c[s]=Math.abs(f*(1-n));t.Envelope.Type={linear:"linear",exponential:"exponential",bounce:{In:e(c),Out:c},cosine:{In:_,Out:i(_)},step:{In:h,Out:e(h)},ripple:{In:o,Out:e(o)},sine:{In:u,Out:e(u)}}}(),t.Envelope.prototype.dispose=function(){return t.prototype.dispose.call(this),this._sig.dispose(),this._sig=null,this._attackCurve=null,this._releaseCurve=null,this},t.Envelope}),e(function(t){return t.AmplitudeEnvelope=function(){t.Envelope.apply(this,arguments),this.input=this.output=new t.Gain,this._sig.connect(this.output.gain)},t.extend(t.AmplitudeEnvelope,t.Envelope),t.AmplitudeEnvelope.prototype.dispose=function(){return this.input.dispose(),this.input=null,t.Envelope.prototype.dispose.call(this),this},t.AmplitudeEnvelope}),e(function(t){return t.Analyser=function(){var e=this.optionsObject(arguments,["type","size"],t.Analyser.defaults);this._analyser=this.input=this.output=this.context.createAnalyser(),this._type=e.type,this._returnType=e.returnType,this._buffer=null,this.size=e.size,this.type=e.type,this.returnType=e.returnType,this.minDecibels=e.minDecibels,this.maxDecibels=e.maxDecibels},t.extend(t.Analyser),t.Analyser.defaults={size:1024,returnType:"byte",type:"fft",smoothing:.8,maxDecibels:-30,minDecibels:-100},t.Analyser.Type={Waveform:"waveform",FFT:"fft"},t.Analyser.ReturnType={Byte:"byte",Float:"float"},t.Analyser.prototype.analyse=function(){var e,i;if(this._type===t.Analyser.Type.FFT)this._returnType===t.Analyser.ReturnType.Byte?this._analyser.getByteFrequencyData(this._buffer):this._analyser.getFloatFrequencyData(this._buffer);else if(this._type===t.Analyser.Type.Waveform)if(this._returnType===t.Analyser.ReturnType.Byte)this._analyser.getByteTimeDomainData(this._buffer);else if(this.isFunction(AnalyserNode.prototype.getFloatTimeDomainData))this._analyser.getFloatTimeDomainData(this._buffer);else for(e=new Uint8Array(this._buffer.length),this._analyser.getByteTimeDomainData(e),i=0;i=t?0:1},127),this._scale=this.input=new t.Multiply(1e4),this._scale.connect(this._thresh)},t.extend(t.GreaterThanZero,t.SignalBase),t.GreaterThanZero.prototype.dispose=function(){return t.prototype.dispose.call(this),this._scale.dispose(),this._scale=null,this._thresh.dispose(),this._thresh=null,this},t.GreaterThanZero}),e(function(t){return t.GreaterThan=function(e){t.call(this,2,0),this._param=this.input[0]=new t.Subtract(e),this.input[1]=this._param.input[1],this._gtz=this.output=new t.GreaterThanZero,this._param.connect(this._gtz)},t.extend(t.GreaterThan,t.Signal),t.GreaterThan.prototype.dispose=function(){return t.prototype.dispose.call(this),this._param.dispose(),this._param=null,this._gtz.dispose(),this._gtz=null,this},t.GreaterThan}),e(function(t){return t.Abs=function(){t.call(this,1,0),this._abs=this.input=this.output=new t.WaveShaper(function(t){return 0===t?0:Math.abs(t)},127)},t.extend(t.Abs,t.SignalBase),t.Abs.prototype.dispose=function(){return t.prototype.dispose.call(this),this._abs.dispose(),this._abs=null,this},t.Abs}),e(function(t){return t.Modulo=function(e){t.call(this,1,1),this._shaper=new t.WaveShaper(Math.pow(2,16)),this._multiply=new t.Multiply,this._subtract=this.output=new t.Subtract,this._modSignal=new t.Signal(e),this.input.fan(this._shaper,this._subtract),this._modSignal.connect(this._multiply,0,0),this._shaper.connect(this._multiply,0,1),this._multiply.connect(this._subtract,0,1),this._setWaveShaper(e)},t.extend(t.Modulo,t.SignalBase),t.Modulo.prototype._setWaveShaper=function(t){this._shaper.setMap(function(e){var i=Math.floor((e+1e-4)/t);return i})},Object.defineProperty(t.Modulo.prototype,"value",{get:function(){return this._modSignal.value},set:function(t){this._modSignal.value=t,this._setWaveShaper(t)}}),t.Modulo.prototype.dispose=function(){return t.prototype.dispose.call(this),this._shaper.dispose(),this._shaper=null,this._multiply.dispose(),this._multiply=null,this._subtract.dispose(),this._subtract=null,this._modSignal.dispose(),this._modSignal=null,this},t.Modulo}),e(function(t){return t.AudioToGain=function(){this._norm=this.input=this.output=new t.WaveShaper(function(t){return(t+1)/2})},t.extend(t.AudioToGain,t.SignalBase),t.AudioToGain.prototype.dispose=function(){return t.prototype.dispose.call(this),this._norm.dispose(),this._norm=null,this},t.AudioToGain}),e(function(t){function e(t,e,i){var s=new t;return i._eval(e[0]).connect(s,0,0),i._eval(e[1]).connect(s,0,1),s}function i(t,e,i){var s=new t;return i._eval(e[0]).connect(s,0,0),s}function s(t){return t?parseFloat(t):void 0}function n(t){return t&&t.args?parseFloat(t.args):void 0}return t.Expr=function(){var t,e,i,s=this._replacements(Array.prototype.slice.call(arguments)),n=this._parseInputs(s);for(this._nodes=[],this.input=new Array(n),t=0;n>t;t++)this.input[t]=this.context.createGain();e=this._parseTree(s);try{i=this._eval(e)}catch(o){throw this._disposeNodes(),new Error("Tone.Expr: Could evaluate expression: "+s)}this.output=i},t.extend(t.Expr,t.SignalBase),t.Expr._Expressions={value:{signal:{regexp:/^\d+\.\d+|^\d+/,method:function(e){var i=new t.Signal(s(e));return i}},input:{regexp:/^\$\d/,method:function(t,e){return e.input[s(t.substr(1))]}}},glue:{"(":{regexp:/^\(/},")":{regexp:/^\)/},",":{regexp:/^,/}},func:{abs:{regexp:/^abs/,method:i.bind(this,t.Abs)},mod:{regexp:/^mod/,method:function(e,i){var s=n(e[1]),o=new t.Modulo(s);return i._eval(e[0]).connect(o),o}},pow:{regexp:/^pow/,method:function(e,i){var s=n(e[1]),o=new t.Pow(s);return i._eval(e[0]).connect(o),o}},a2g:{regexp:/^a2g/,method:function(e,i){var s=new t.AudioToGain;return i._eval(e[0]).connect(s),s}}},binary:{"+":{regexp:/^\+/,precedence:1,method:e.bind(this,t.Add)},"-":{regexp:/^\-/,precedence:1,method:function(s,n){return 1===s.length?i(t.Negate,s,n):e(t.Subtract,s,n)}},"*":{regexp:/^\*/,precedence:0,method:e.bind(this,t.Multiply)}},unary:{"-":{regexp:/^\-/,method:i.bind(this,t.Negate)},"!":{regexp:/^\!/,method:i.bind(this,t.NOT)}}},t.Expr.prototype._parseInputs=function(t){var e,i,s=t.match(/\$\d/g),n=0;if(null!==s)for(e=0;e0;)e=e.trim(),s=i(e),o.push(s),e=e.substr(s.value.length);return{next:function(){return o[++n]},peek:function(){return o[n+1]}}},t.Expr.prototype._parseTree=function(e){function i(t,e){return!u(t)&&"glue"===t.type&&t.value===e}function s(e,i,s){var n,o,r=!1,a=t.Expr._Expressions[i];if(!u(e))for(n in a)if(o=a[n],o.regexp.test(e.value)){if(u(s))return!0;if(o.precedence===s)return!0}return r}function n(t){var e,i;for(u(t)&&(t=5),e=0>t?o():n(t-1),i=l.peek();s(i,"binary",t);)i=l.next(),e={operator:i.value,method:i.method,args:[e,n(t-1)]},i=l.peek();return e}function o(){var t,e;return t=l.peek(),s(t,"unary")?(t=l.next(),e=o(),{operator:t.value,method:t.method,args:[e]}):r()}function r(){var t,e;if(t=l.peek(),u(t))throw new SyntaxError("Tone.Expr: Unexpected termination of expression");if("func"===t.type)return t=l.next(),a(t);if("value"===t.type)return t=l.next(),{method:t.method,args:t.value};if(i(t,"(")){if(l.next(),e=n(),t=l.next(),!i(t,")"))throw new SyntaxError("Expected )");return e}throw new SyntaxError("Tone.Expr: Parse error, cannot process token "+t.value)}function a(t){var e,s=[];if(e=l.next(),!i(e,"("))throw new SyntaxError('Tone.Expr: Expected ( in a function call "'+t.value+'"');if(e=l.peek(),i(e,")")||(s=h()),e=l.next(),!i(e,")"))throw new SyntaxError('Tone.Expr: Expected ) in a function call "'+t.value+'"');return{method:t.method,args:s,name:name}}function h(){for(var t,e,s=[];;){if(e=n(),u(e))break;if(s.push(e),t=l.peek(),!i(t,","))break;l.next()}return s}var l=this._tokenize(e),u=this.isUndef.bind(this);return n()},t.Expr.prototype._eval=function(t){if(!this.isUndef(t)){var e=t.method(t.args,this);return this._nodes.push(e),e}},t.Expr.prototype._disposeNodes=function(){var t,e;for(t=0;tn;n++)o=this.context.createBiquadFilter(),o.type=this._type,this.frequency.connect(o.frequency),this.detune.connect(o.detune),this.Q.connect(o.Q),this.gain.connect(o.gain),this._filters[n]=o;r=[this.input].concat(this._filters).concat([this.output]),this.connectSeries.apply(this,r)}}),t.Filter.prototype.dispose=function(){t.prototype.dispose.call(this);for(var e=0;e=t?e:i})},Object.defineProperty(t.Follower.prototype,"attack",{get:function(){return this._attack},set:function(t){this._attack=t,this._setAttackRelease(this._attack,this._release)}}),Object.defineProperty(t.Follower.prototype,"release",{get:function(){return this._release},set:function(t){this._release=t,this._setAttackRelease(this._attack,this._release)}}),t.Follower.prototype.connect=t.Signal.prototype.connect,t.Follower.prototype.dispose=function(){return t.prototype.dispose.call(this),this._filter.disconnect(),this._filter=null,this._frequencyValues.disconnect(),this._frequencyValues=null,this._delay.disconnect(),this._delay=null,this._sub.disconnect(),this._sub=null,this._abs.dispose(),this._abs=null,this._mult.dispose(),this._mult=null,this._curve=null,this},t.Follower}),e(function(t){return t.ScaledEnvelope=function(){var e=this.optionsObject(arguments,["attack","decay","sustain","release"],t.Envelope.defaults);t.Envelope.call(this,e),e=this.defaultArg(e,t.ScaledEnvelope.defaults),this._exp=this.output=new t.Pow(e.exponent),this._scale=this.output=new t.Scale(e.min,e.max),this._sig.chain(this._exp,this._scale)},t.extend(t.ScaledEnvelope,t.Envelope),t.ScaledEnvelope.defaults={min:0,max:1,exponent:1},Object.defineProperty(t.ScaledEnvelope.prototype,"min",{get:function(){return this._scale.min},set:function(t){this._scale.min=t}}),Object.defineProperty(t.ScaledEnvelope.prototype,"max",{get:function(){return this._scale.max},set:function(t){this._scale.max=t}}),Object.defineProperty(t.ScaledEnvelope.prototype,"exponent",{get:function(){return this._exp.value},set:function(t){this._exp.value=t}}),t.ScaledEnvelope.prototype.dispose=function(){return t.Envelope.prototype.dispose.call(this),this._scale.dispose(),this._scale=null,this._exp.dispose(),this._exp=null,this},t.ScaledEnvelope}),e(function(t){return t.FrequencyEnvelope=function(){var e=this.optionsObject(arguments,["attack","decay","sustain","release"],t.Envelope.defaults);t.ScaledEnvelope.call(this,e),e=this.defaultArg(e,t.FrequencyEnvelope.defaults),this._octaves=e.octaves,this.baseFrequency=e.baseFrequency,this.octaves=e.octaves},t.extend(t.FrequencyEnvelope,t.Envelope),t.FrequencyEnvelope.defaults={baseFrequency:200,octaves:4,exponent:2},Object.defineProperty(t.FrequencyEnvelope.prototype,"baseFrequency",{get:function(){return this._scale.min},set:function(t){this._scale.min=this.toFrequency(t)}}),Object.defineProperty(t.FrequencyEnvelope.prototype,"octaves",{get:function(){return this._octaves},set:function(t){this._octaves=t,this._scale.max=this.baseFrequency*Math.pow(2,t)}}),Object.defineProperty(t.FrequencyEnvelope.prototype,"exponent",{get:function(){return this._exp.value},set:function(t){this._exp.value=t}}),t.FrequencyEnvelope.prototype.dispose=function(){return t.ScaledEnvelope.prototype.dispose.call(this),this},t.FrequencyEnvelope}),e(function(t){return t.Gate=function(){t.call(this);var e=this.optionsObject(arguments,["threshold","attack","release"],t.Gate.defaults);this._follower=new t.Follower(e.attack,e.release),this._gt=new t.GreaterThan(this.dbToGain(e.threshold)),this.input.connect(this.output),this.input.chain(this._gt,this._follower,this.output.gain)},t.extend(t.Gate),t.Gate.defaults={attack:.1,release:.1,threshold:-40},Object.defineProperty(t.Gate.prototype,"threshold",{get:function(){return this.gainToDb(this._gt.value)},set:function(t){this._gt.value=this.dbToGain(t)}}),Object.defineProperty(t.Gate.prototype,"attack",{get:function(){return this._follower.attack},set:function(t){this._follower.attack=t}}),Object.defineProperty(t.Gate.prototype,"release",{get:function(){return this._follower.release},set:function(t){this._follower.release=t}}),t.Gate.prototype.dispose=function(){return t.prototype.dispose.call(this),this._follower.dispose(),this._gt.dispose(),this._follower=null,this._gt=null,this},t.Gate}),e(function(t){return t.TimelineState=function(e){t.Timeline.call(this),this._initial=e},t.extend(t.TimelineState,t.Timeline),t.TimelineState.prototype.getStateAtTime=function(t){ +var e=this.getEvent(t);return null!==e?e.state:this._initial},t.TimelineState.prototype.setStateAtTime=function(t,e){this.addEvent({state:t,time:this.toSeconds(e)})},t.TimelineState}),e(function(t){var e,i,s;return t.Clock=function(){var i=this.optionsObject(arguments,["callback","frequency"],t.Clock.defaults);this.callback=i.callback,this._lookAhead="auto",this._computedLookAhead=e/1e3,this._nextTick=-1,this._lastUpdate=-1,this._loopID=-1,this.frequency=new t.TimelineSignal(i.frequency,t.Type.Frequency),this.ticks=0,this._state=new t.TimelineState(t.State.Stopped),this._boundLoop=this._loop.bind(this),t.Clock._worker.addEventListener("message",this._boundLoop),this._readOnly("frequency")},t.extend(t.Clock),t.Clock.defaults={callback:t.noOp,frequency:1,lookAhead:"auto"},Object.defineProperty(t.Clock.prototype,"state",{get:function(){return this._state.getStateAtTime(this.now())}}),Object.defineProperty(t.Clock.prototype,"lookAhead",{get:function(){return this._lookAhead},set:function(t){"auto"===t?this._lookAhead="auto":this._lookAhead=this.toSeconds(t)}}),t.Clock.prototype.start=function(e,i){return e=this.toSeconds(e),this._state.getStateAtTime(e)!==t.State.Started&&this._state.addEvent({state:t.State.Started,time:e,offset:i}),this},t.Clock.prototype.stop=function(e){return e=this.toSeconds(e),this._state.cancel(e),this._state.setStateAtTime(t.State.Stopped,e),this},t.Clock.prototype.pause=function(e){return e=this.toSeconds(e),this._state.getStateAtTime(e)===t.State.Started&&this._state.setStateAtTime(t.State.Paused,e),this},t.Clock.prototype._loop=function(){var i,s,n,o,r,a,h;if("auto"===this._lookAhead?(i=this.now(),-1!==this._lastUpdate&&(s=i-this._lastUpdate,s=Math.min(10*e/1e3,s),this._computedLookAhead=(9*this._computedLookAhead+s)/10),this._lastUpdate=i):this._computedLookAhead=this._lookAhead,n=this.now(),o=2*this._computedLookAhead,r=this._state.getEvent(n+o),a=t.State.Stopped,r&&(a=r.state,-1===this._nextTick&&a===t.State.Started&&(this._nextTick=r.time,this.isUndef(r.offset)||(this.ticks=r.offset))),a===t.State.Started)for(;n+o>this._nextTick;)h=this._nextTick,this._nextTick+=1/this.frequency.getValueAtTime(this._nextTick),this.callback(h),this.ticks++;else a===t.State.Stopped?(this._nextTick=-1,this.ticks=0):a===t.State.Paused&&(this._nextTick=-1)},t.Clock.prototype.getStateAtTime=function(t){return this._state.getStateAtTime(t)},t.Clock.prototype.dispose=function(){cancelAnimationFrame(this._loopID),t.TimelineState.prototype.dispose.call(this),t.Clock._worker.removeEventListener("message",this._boundLoop),this._writable("frequency"),this.frequency.dispose(),this.frequency=null,this._boundLoop=null,this._nextTick=1/0,this.callback=null,this._state.dispose(),this._state=null},window.URL=window.URL||window.webkitURL,e=20,i=new Blob(["setInterval(function(){self.postMessage('tick')}, "+e+")"]),s=URL.createObjectURL(i),t.Clock._worker=new Worker(s),t.Clock}),e(function(t){return t.Emitter=function(){this._events={}},t.extend(t.Emitter),t.Emitter.prototype.on=function(t,e){var i,s,n=t.split(/\W+/);for(i=0;is;s++)i[s].apply(this,e);return this},t.Emitter.mixin=function(e){var i,s,n,o=["on","off","trigger"];for(e._events={},i=0;i0)if(null===t.left.right)i=t.left,i.right=t.right,s=i;else{for(i=t.left.right;null!==i.right;)i=i.right;i.parent.right=i.left,s=i.parent,i.left=t.left,i.right=t.right}else if(null===t.right.left)i=t.right,i.left=t.left,s=i;else{for(i=t.right.left;null!==i.left;)i=i.left;i.parent=i.parent,i.parent.left=i.right,s=i.parent,i.left=t.left,i.right=t.right}null!==t.parent?t.isLeftChild()?t.parent.left=i:t.parent.right=i:this._setRoot(i),this._rebalance(s)}t.dispose()},t.IntervalTimeline.prototype._rotateLeft=function(t){var e=t.parent,i=t.isLeftChild(),s=t.right;t.right=s.left,s.left=t,null!==e?i?e.left=s:e.right=s:this._setRoot(s)},t.IntervalTimeline.prototype._rotateRight=function(t){var e=t.parent,i=t.isLeftChild(),s=t.left;t.left=s.right,s.right=t,null!==e?i?e.left=s:e.right=s:this._setRoot(s)},t.IntervalTimeline.prototype._rebalance=function(t){var e=t.getBalance();e>1?t.left.getBalance()<0?this._rotateLeft(t.left):this._rotateRight(t):-1>e&&(t.right.getBalance()>0?this._rotateRight(t.right):this._rotateLeft(t))},t.IntervalTimeline.prototype.getEvent=function(t){var e,i,s;if(null!==this._root&&(e=[],this._root.search(t,e),e.length>0)){for(i=e[0],s=1;si.low&&(i=e[s]);return i.event}return null},t.IntervalTimeline.prototype.forEach=function(t){var e,i,s;if(null!==this._root)for(e=[],null!==this._root&&this._root.traverse(function(t){e.push(t)}),i=0;i=0;s--)n=i[s].event,n&&e(n);return this},t.IntervalTimeline.prototype.forEachAfter=function(t,e){var i,s,n;if(t=this.toSeconds(t),null!==this._root)for(i=[],this._root.searchAfter(t,i),s=i.length-1;s>=0;s--)n=i[s].event,n&&e(n);return this},t.IntervalTimeline.prototype.dispose=function(){var t,e=[];for(null!==this._root&&this._root.traverse(function(t){e.push(t)}),t=0;tthis.max||(null!==this.left&&this.left.search(t,e),this.low<=t&&this.high>t&&e.push(this),this.low>t||null!==this.right&&this.right.search(t,e))},e.prototype.searchAfter=function(t,e){this.low>=t&&(e.push(this),null!==this.left&&this.left.searchAfter(t,e)),null!==this.right&&this.right.searchAfter(t,e)},e.prototype.traverse=function(t){t(this),null!==this.left&&this.left.traverse(t),null!==this.right&&this.right.traverse(t)},e.prototype.updateHeight=function(){null!==this.left&&null!==this.right?this.height=Math.max(this.left.height,this.right.height)+1:null!==this.right?this.height=this.right.height+1:null!==this.left?this.height=this.left.height+1:this.height=0},e.prototype.updateMax=function(){this.max=this.high,null!==this.left&&(this.max=Math.max(this.max,this.left.max)),null!==this.right&&(this.max=Math.max(this.max,this.right.max))},e.prototype.getBalance=function(){var t=0;return null!==this.left&&null!==this.right?t=this.left.height-this.right.height:null!==this.left?t=this.left.height+1:null!==this.right&&(t=-(this.right.height+1)),t},e.prototype.isLeftChild=function(){return null!==this.parent&&this.parent.left===this},Object.defineProperty(e.prototype,"left",{get:function(){return this._left},set:function(t){this._left=t,null!==t&&(t.parent=this),this.updateHeight(),this.updateMax()}}),Object.defineProperty(e.prototype,"right",{get:function(){return this._right},set:function(t){this._right=t,null!==t&&(t.parent=this),this.updateHeight(),this.updateMax()}}),e.prototype.dispose=function(){this.parent=null,this._left=null,this._right=null,this.event=null},t.IntervalTimeline}),e(function(t){t.Transport=function(){t.Emitter.call(this),this.loop=!1,this._loopStart=0,this._loopEnd=0,this._ppq=e.defaults.PPQ,this._clock=new t.Clock({callback:this._processTick.bind(this),frequency:0}),this.bpm=this._clock.frequency,this.bpm._toUnits=this._toUnits.bind(this),this.bpm._fromUnits=this._fromUnits.bind(this),this.bpm.units=t.Type.BPM,this.bpm.value=e.defaults.bpm,this._readOnly("bpm"),this._timeSignature=e.defaults.timeSignature,this._scheduledEvents={},this._eventID=0,this._timeline=new t.Timeline,this._repeatedEvents=new t.IntervalTimeline,this._onceEvents=new t.Timeline,this._syncedSignals=[],this._swingTicks=e.defaults.PPQ/2,this._swingAmount=0},t.extend(t.Transport,t.Emitter),t.Transport.defaults={bpm:120,swing:0,swingSubdivision:"8n",timeSignature:4,loopStart:0,loopEnd:"4m",PPQ:192},t.Transport.prototype._processTick=function(e){var i,s,n=this._clock.ticks;this._swingAmount>0&&n%this._ppq!==0&&n%(2*this._swingTicks)!==0&&(i=n%(2*this._swingTicks)/(2*this._swingTicks),s=Math.sin(i*Math.PI)*this._swingAmount,e+=t.Time(2*this._swingTicks/3,"i").eval()*s),this.loop&&n===this._loopEnd&&(this.ticks=this._loopStart,n=this._loopStart,this.trigger("loop",e)),this._onceEvents.forEachBefore(n,function(t){t.callback(e)}),this._onceEvents.cancelBefore(n),this._timeline.forEachAtTime(n,function(t){t.callback(e)}),this._repeatedEvents.forEachAtTime(n,function(t){(n-t.time)%t.interval===0&&t.callback(e)})},t.Transport.prototype.schedule=function(t,e){var i={time:this.toTicks(e),callback:t},s=this._eventID++;return this._scheduledEvents[s.toString()]={event:i,timeline:this._timeline},this._timeline.addEvent(i),s},t.Transport.prototype.scheduleRepeat=function(t,e,i,s){var n,o;if(0>=e)throw new Error("Tone.Transport: repeat events must have an interval larger than 0");return n={time:this.toTicks(i),duration:this.toTicks(this.defaultArg(s,1/0)),interval:this.toTicks(e),callback:t},o=this._eventID++,this._scheduledEvents[o.toString()]={event:n,timeline:this._repeatedEvents},this._repeatedEvents.addEvent(n),o},t.Transport.prototype.scheduleOnce=function(t,e){var i={time:this.toTicks(e),callback:t},s=this._eventID++;return this._scheduledEvents[s.toString()]={event:i,timeline:this._onceEvents},this._onceEvents.addEvent(i),s},t.Transport.prototype.clear=function(t){if(this._scheduledEvents.hasOwnProperty(t)){var e=this._scheduledEvents[t.toString()];e.timeline.removeEvent(e.event),delete this._scheduledEvents[t.toString()]}return this},t.Transport.prototype.cancel=function(t){return t=this.defaultArg(t,0),t=this.toTicks(t),this._timeline.cancel(t),this._onceEvents.cancel(t),this._repeatedEvents.cancel(t),this},Object.defineProperty(t.Transport.prototype,"state",{get:function(){return this._clock.getStateAtTime(this.now())}}),t.Transport.prototype.start=function(e,i){return e=this.toSeconds(e),i=this.isUndef(i)?new t.Time(this._clock.ticks,"i"):new t.Time(i),this._clock.start(e,i.toTicks()),this.trigger("start",e,i.toSeconds()),this},t.Transport.prototype.stop=function(t){return t=this.toSeconds(t),this._clock.stop(t),this.trigger("stop",t),this},t.Transport.prototype.pause=function(t){return t=this.toSeconds(t),this._clock.pause(t),this.trigger("pause",t),this},Object.defineProperty(t.Transport.prototype,"timeSignature",{get:function(){return this._timeSignature},set:function(t){this.isArray(t)&&(t=t[0]/t[1]*4),this._timeSignature=t}}),Object.defineProperty(t.Transport.prototype,"loopStart",{get:function(){return t.TransportTime(this._loopStart,"i").toSeconds()},set:function(t){this._loopStart=this.toTicks(t)}}),Object.defineProperty(t.Transport.prototype,"loopEnd",{get:function(){return t.TransportTime(this._loopEnd,"i").toSeconds()},set:function(t){this._loopEnd=this.toTicks(t)}}),t.Transport.prototype.setLoopPoints=function(t,e){return this.loopStart=t,this.loopEnd=e,this},Object.defineProperty(t.Transport.prototype,"swing",{get:function(){return this._swingAmount},set:function(t){this._swingAmount=t}}),Object.defineProperty(t.Transport.prototype,"swingSubdivision",{get:function(){return t.Time(this._swingTicks,"i").toNotation()},set:function(t){this._swingTicks=this.toTicks(t)}}),Object.defineProperty(t.Transport.prototype,"position",{get:function(){return t.TransportTime(this.ticks,"i").toBarsBeatsSixteenths()},set:function(t){var e=this.toTicks(t);this.ticks=e}}),Object.defineProperty(t.Transport.prototype,"progress",{get:function(){return this.loop?(this.ticks-this._loopStart)/(this._loopEnd-this._loopStart):0}}),Object.defineProperty(t.Transport.prototype,"ticks",{get:function(){return this._clock.ticks},set:function(t){this._clock.ticks=t}}),Object.defineProperty(t.Transport.prototype,"PPQ",{get:function(){return this._ppq},set:function(t){var e=this.bpm.value;this._ppq=t,this.bpm.value=e}}),t.Transport.prototype._fromUnits=function(t){return 1/(60/t/this.PPQ)},t.Transport.prototype._toUnits=function(t){return t/this.PPQ*60},t.Transport.prototype.nextSubdivision=function(e){var i,s,n;return e=this.toSeconds(e),this.state!==t.State.Started?0:(i=this._clock._nextTick,s=t.Time(this.ticks,"i").eval(),n=e-s%e,0===n&&(n=e),i+n)},t.Transport.prototype.syncSignal=function(e,i){i||(i=0!==e._param.value?e._param.value/this.bpm._param.value:0);var s=new t.Gain(i);return this.bpm.chain(s,e._param),this._syncedSignals.push({ratio:s,signal:e,initial:e._param.value}),e._param.value=0,this},t.Transport.prototype.unsyncSignal=function(t){var e,i;for(e=this._syncedSignals.length-1;e>=0;e--)i=this._syncedSignals[e],i.signal===t&&(i.ratio.dispose(),i.signal._param.value=i.initial,this._syncedSignals.splice(e,1));return this},t.Transport.prototype.dispose=function(){return t.Emitter.prototype.dispose.call(this),this._clock.dispose(),this._clock=null,this._writable("bpm"),this.bpm=null,this._timeline.dispose(),this._timeline=null,this._onceEvents.dispose(),this._onceEvents=null,this._repeatedEvents.dispose(),this._repeatedEvents=null,this};var e=t.Transport;return t._initAudioContext(function(){if("function"==typeof t.Transport)t.Transport=new t.Transport;else{t.Transport.stop();var i=t.Transport.get();t.Transport.dispose(),e.call(t.Transport),t.Transport.set(i)}}),t.Transport}),e(function(t){return t.Volume=function(){var e=this.optionsObject(arguments,["volume"],t.Volume.defaults);this.output=this.input=new t.Gain(e.volume,t.Type.Decibels),this._unmutedVolume=0,this._muted=!1,this.volume=this.output.gain,this._readOnly("volume"),this.mute=e.mute},t.extend(t.Volume),t.Volume.defaults={volume:0,mute:!1},Object.defineProperty(t.Volume.prototype,"mute",{get:function(){return this._muted},set:function(t){!this._muted&&t?(this._unmutedVolume=this.volume.value,this.volume.value=-(1/0)):this._muted&&!t&&(this.volume.value=this._unmutedVolume),this._muted=t}}),t.Volume.prototype.dispose=function(){return this.input.dispose(),t.prototype.dispose.call(this),this._writable("volume"),this.volume.dispose(),this.volume=null,this},t.Volume}),e(function(t){t.Master=function(){t.call(this),this._volume=this.output=new t.Volume,this.volume=this._volume.volume,this._readOnly("volume"),this.input.chain(this.output,this.context.destination)},t.extend(t.Master),t.Master.defaults={volume:0,mute:!1},Object.defineProperty(t.Master.prototype,"mute",{get:function(){return this._volume.mute},set:function(t){this._volume.mute=t}}),t.Master.prototype.chain=function(){this.input.disconnect(),this.input.chain.apply(this.input,arguments),arguments[arguments.length-1].connect(this.output)},t.Master.prototype.dispose=function(){t.prototype.dispose.call(this),this._writable("volume"),this._volume.dispose(),this._volume=null,this.volume=null},t.prototype.toMaster=function(){return this.connect(t.Master),this},AudioNode.prototype.toMaster=function(){return this.connect(t.Master),this};var e=t.Master;return t._initAudioContext(function(){t.prototype.isUndef(t.Master)?(e.prototype.dispose.call(t.Master),e.call(t.Master)):t.Master=new e}),t.Master}),e(function(t){return t.Source=function(e){t.call(this),e=this.defaultArg(e,t.Source.defaults),this._volume=this.output=new t.Volume(e.volume),this.volume=this._volume.volume,this._readOnly("volume"),this._state=new t.TimelineState(t.State.Stopped),this._state.memory=10,this._syncStart=function(t,e){t=this.toSeconds(t),t+=this.toSeconds(this._startDelay),this.start(t,e)}.bind(this),this._syncStop=this.stop.bind(this),this._startDelay=0,this._volume.output.output.channelCount=2,this._volume.output.output.channelCountMode="explicit",this.mute=e.mute},t.extend(t.Source),t.Source.defaults={volume:0,mute:!1},Object.defineProperty(t.Source.prototype,"state",{get:function(){return this._state.getStateAtTime(this.now())}}),Object.defineProperty(t.Source.prototype,"mute",{get:function(){return this._volume.mute},set:function(t){this._volume.mute=t}}),t.Source.prototype.start=function(e){return e=this.toSeconds(e),(this._state.getStateAtTime(e)!==t.State.Started||this.retrigger)&&(this._state.setStateAtTime(t.State.Started,e),this._start&&this._start.apply(this,arguments)),this},t.Source.prototype.stop=function(e){return e=this.toSeconds(e),this._state.cancel(e),this._state.setStateAtTime(t.State.Stopped,e),this._stop&&this._stop.apply(this,arguments),this},t.Source.prototype.sync=function(e){return this._startDelay=this.defaultArg(e,0),t.Transport.on("start",this._syncStart),t.Transport.on("stop pause",this._syncStop),this},t.Source.prototype.unsync=function(){return this._startDelay=0,t.Transport.off("start",this._syncStart),t.Transport.off("stop pause",this._syncStop),this},t.Source.prototype.dispose=function(){this.stop(),t.prototype.dispose.call(this),this.unsync(),this._writable("volume"),this._volume.dispose(),this._volume=null,this.volume=null,this._state.dispose(),this._state=null,this._syncStart=null,this._syncStart=null},t.Source}),e(function(t){return t.Oscillator=function(){var e=this.optionsObject(arguments,["frequency","type"],t.Oscillator.defaults);t.Source.call(this,e),this._oscillator=null,this.frequency=new t.Signal(e.frequency,t.Type.Frequency),this.detune=new t.Signal(e.detune,t.Type.Cents),this._wave=null,this._partials=this.defaultArg(e.partials,[1]),this._phase=e.phase,this._type=null,this.type=e.type,this.phase=this._phase,this._readOnly(["frequency","detune"])},t.extend(t.Oscillator,t.Source),t.Oscillator.defaults={type:"sine",frequency:440,detune:0,phase:0,partials:[]},t.Oscillator.Type={Sine:"sine",Triangle:"triangle",Sawtooth:"sawtooth",Square:"square",Custom:"custom"},t.Oscillator.prototype._start=function(t){this._oscillator=this.context.createOscillator(),this._oscillator.setPeriodicWave(this._wave),this._oscillator.connect(this.output),this.frequency.connect(this._oscillator.frequency),this.detune.connect(this._oscillator.detune),this._oscillator.start(this.toSeconds(t))},t.Oscillator.prototype._stop=function(t){return this._oscillator&&(this._oscillator.stop(this.toSeconds(t)),this._oscillator=null),this},t.Oscillator.prototype.syncFrequency=function(){return t.Transport.syncSignal(this.frequency),this},t.Oscillator.prototype.unsyncFrequency=function(){return t.Transport.unsyncSignal(this.frequency),this},Object.defineProperty(t.Oscillator.prototype,"type",{get:function(){return this._type},set:function(t){var e=this._getRealImaginary(t,this._phase),i=this.context.createPeriodicWave(e[0],e[1]);this._wave=i,null!==this._oscillator&&this._oscillator.setPeriodicWave(this._wave),this._type=t}}),t.Oscillator.prototype._getRealImaginary=function(e,i){var s,n,o,r,a=4096,h=a/2,l=new Float32Array(h),u=new Float32Array(h),c=1;for(e===t.Oscillator.Type.Custom?(c=this._partials.length+1,h=c):(s=/^(sine|triangle|square|sawtooth)(\d+)$/.exec(e),s&&(c=parseInt(s[2])+1,e=s[1],c=Math.max(c,2),h=c)),n=1;h>n;++n){switch(o=2/(n*Math.PI),e){case t.Oscillator.Type.Sine:r=c>=n?1:0;break;case t.Oscillator.Type.Square:r=1&n?2*o:0;break;case t.Oscillator.Type.Sawtooth:r=o*(1&n?1:-1);break;case t.Oscillator.Type.Triangle:r=1&n?2*(o*o)*(n-1>>1&1?-1:1):0;break;case t.Oscillator.Type.Custom:r=this._partials[n-1];break;default:throw new TypeError("Tone.Oscillator: invalid type: "+e)}0!==r?(l[n]=-r*Math.sin(i*n),u[n]=r*Math.cos(i*n)):(l[n]=0,u[n]=0)}return[l,u]},t.Oscillator.prototype._inverseFFT=function(t,e,i){var s,n=0,o=t.length;for(s=0;o>s;s++)n+=t[s]*Math.cos(s*i)+e[s]*Math.sin(s*i);return n},t.Oscillator.prototype._getInitialValue=function(){var t,e=this._getRealImaginary(this._type,0),i=e[0],s=e[1],n=0,o=2*Math.PI;for(t=0;8>t;t++)n=Math.max(this._inverseFFT(i,s,t/8*o),n);return-this._inverseFFT(i,s,this._phase)/n},Object.defineProperty(t.Oscillator.prototype,"partials",{get:function(){return this._type!==t.Oscillator.Type.Custom?[]:this._partials},set:function(e){this._partials=e,this.type=t.Oscillator.Type.Custom}}),Object.defineProperty(t.Oscillator.prototype,"phase",{get:function(){return this._phase*(180/Math.PI)},set:function(t){this._phase=t*Math.PI/180,this.type=this._type}}),t.Oscillator.prototype.dispose=function(){return t.Source.prototype.dispose.call(this),null!==this._oscillator&&(this._oscillator.disconnect(),this._oscillator=null),this._wave=null,this._writable(["frequency","detune"]),this.frequency.dispose(),this.frequency=null,this.detune.dispose(),this.detune=null,this._partials=null,this},t.Oscillator}),e(function(t){return t.Zero=function(){this._gain=this.input=this.output=new t.Gain,t.Zero._zeros.connect(this._gain)},t.extend(t.Zero),t.Zero.prototype.dispose=function(){return t.prototype.dispose.call(this),this._gain.dispose(),this._gain=null,this},t.Zero._zeros=null,t._initAudioContext(function(e){var i,s=e.createBuffer(1,128,e.sampleRate),n=s.getChannelData(0);for(i=0;is&&s+o>i&&(r=t[n],this.isObject(r)?this.value=r.value:this.value=r),s+=o;else this.value=t;return this.value},t.CtrlMarkov.prototype._getProbDistribution=function(t){var e,i,s,n=[],o=0,r=!1;for(e=0;e=this.values.length&&(this.index=0)):e===t.CtrlPattern.Type.Down?(this.index--,this.index<0&&(this.index=this.values.length-1)):e===t.CtrlPattern.Type.UpDown||e===t.CtrlPattern.Type.DownUp?(this._direction===t.CtrlPattern.Type.Up?this.index++:this.index--,this.index<0?(this.index=1,this._direction=t.CtrlPattern.Type.Up):this.index>=this.values.length&&(this.index=this.values.length-2,this._direction=t.CtrlPattern.Type.Down)):e===t.CtrlPattern.Type.Random?this.index=Math.floor(Math.random()*this.values.length):e===t.CtrlPattern.Type.RandomWalk?Math.random()<.5?(this.index--,this.index=Math.max(this.index,0)):(this.index++,this.index=Math.min(this.index,this.values.length-1)):e===t.CtrlPattern.Type.RandomOnce?(this.index++,this.index>=this.values.length&&(this.index=0,this._shuffleValues())):e===t.CtrlPattern.Type.AlternateUp?(this._direction===t.CtrlPattern.Type.Up?(this.index+=2,this._direction=t.CtrlPattern.Type.Down):(this.index-=1,this._direction=t.CtrlPattern.Type.Up),this.index>=this.values.length&&(this.index=0,this._direction=t.CtrlPattern.Type.Up)):e===t.CtrlPattern.Type.AlternateDown&&(this._direction===t.CtrlPattern.Type.Up?(this.index+=1,this._direction=t.CtrlPattern.Type.Down):(this.index-=2,this._direction=t.CtrlPattern.Type.Up),this.index<0&&(this.index=this.values.length-1,this._direction=t.CtrlPattern.Type.Down)),this.value},t.CtrlPattern.prototype._shuffleValues=function(){var t,e,i=[];for(this._shuffled=[],t=0;t0;)e=i.splice(Math.floor(i.length*Math.random()),1),this._shuffled.push(e[0])},t.CtrlPattern.prototype.dispose=function(){this._shuffled=null,this.values=null},t.CtrlPattern}),e(function(t){return t.CtrlRandom=function(){var e=this.optionsObject(arguments,["min","max"],t.CtrlRandom.defaults);this.min=e.min,this.max=e.max,this.integer=e.integer},t.extend(t.CtrlRandom),t.CtrlRandom.defaults={min:0,max:1,integer:!1},Object.defineProperty(t.CtrlRandom.prototype,"value",{get:function(){var t=this.toSeconds(this.min),e=this.toSeconds(this.max),i=Math.random(),s=i*t+(1-i)*e;return this.integer&&(s=Math.floor(s)),s}}),t.CtrlRandom}),e(function(t){return t.Buffer=function(){var e=this.optionsObject(arguments,["url","onload"],t.Buffer.defaults);this._buffer=null,this._reversed=e.reverse,this.url=void 0,this.loaded=!1,this.onload=e.onload.bind(this,this),e.url instanceof AudioBuffer||e.url instanceof t.Buffer?(this.set(e.url),this.onload(this)):this.isString(e.url)&&(this.url=e.url,t.Buffer._addToQueue(e.url,this))},t.extend(t.Buffer),t.Buffer.defaults={url:void 0,onload:t.noOp,reverse:!1},t.Buffer.prototype.set=function(e){return e instanceof t.Buffer?this._buffer=e.get():this._buffer=e,this.loaded=!0,this},t.Buffer.prototype.get=function(){return this._buffer},t.Buffer.prototype.load=function(e,i){return this.url=e,this.onload=this.defaultArg(i,this.onload),t.Buffer._addToQueue(e,this),this},t.Buffer.prototype.dispose=function(){return t.prototype.dispose.call(this),t.Buffer._removeFromQueue(this),this._buffer=null,this.onload=t.Buffer.defaults.onload,this},Object.defineProperty(t.Buffer.prototype,"duration",{get:function(){return this._buffer?this._buffer.duration:0}}),t.Buffer.prototype._reverse=function(){if(this.loaded)for(var t=0;t0){if(t.Buffer._currentDownloads.length0){for(e=0;r>e;e++)i=t.Buffer._currentDownloads[e],o+=i.progress;a=o}s=r-a,n=t.Buffer._totalDownloads-t.Buffer._queue.length-s,t.Buffer.trigger("progress",n/t.Buffer._totalDownloads)},t.Buffer.baseUrl="",t.Buffer.load=function(e,i){var s=new XMLHttpRequest;return s.open("GET",t.Buffer.baseUrl+e,!0),s.responseType="arraybuffer",s.onload=function(){t.context.decodeAudioData(s.response,function(t){i(t)},function(){throw new Error("Tone.Buffer: could not decode audio data:"+e)})},s.send(),s},t.Buffer.supportsType=function(t){var e,i=t.split(".");return i=i[i.length-1],e=document.createElement("audio").canPlayType("audio/"+i),""!==e},t.Buffer}),e(function(t){return t.Buffers=function(t,e,i){this._buffers={},this.baseUrl=this.defaultArg(i,""),t=this._flattenUrls(t),this._loadingCount=0;for(var s in t)this._loadingCount++,this.add(s,t[s],this._bufferLoaded.bind(this,e))},t.extend(t.Buffers),t.Buffers.prototype.get=function(t){if(this._buffers.hasOwnProperty(t))return this._buffers[t];throw new Error("Tone.Buffers: no buffer named"+t)},t.Buffers.prototype._bufferLoaded=function(t){this._loadingCount--,0===this._loadingCount&&t&&t(this)},t.Buffers.prototype.add=function(e,i,s){return s=this.defaultArg(s,t.noOp),i instanceof t.Buffer?(this._buffers[e]=i,s(this)):i instanceof AudioBuffer?(this._buffers[e]=new t.Buffer(i),s(this)):this.isString(i)&&(this._buffers[e]=new t.Buffer(this.baseUrl+i,s)),this},t.Buffers.prototype._flattenUrls=function(t){var e,i,s,n={};for(e in t)if(t.hasOwnProperty(e))if(this.isObject(t[e])){i=this._flattenUrls(t[e]);for(s in i)i.hasOwnProperty(s)&&(n[e+"."+s]=i[s])}else n[e]=t[e];return n},t.Buffers.prototype.dispose=function(){for(var t in this._buffers)this._buffers[t].dispose();return this._buffers=null,this},t.Buffers}),e(function(t){var e={};return t.prototype.send=function(t,i){e.hasOwnProperty(t)||(e[t]=this.context.createGain());var s=this.context.createGain();return s.gain.value=this.dbToGain(this.defaultArg(i,1)),this.output.chain(s,e[t]),s},t.prototype.receive=function(t,i){return e.hasOwnProperty(t)||(e[t]=this.context.createGain()),this.isUndef(i)&&(i=this.input),e[t].connect(i),this},t}),e(function(t){return t.Delay=function(){var e=this.optionsObject(arguments,["delayTime","maxDelay"],t.Delay.defaults);this._delayNode=this.input=this.output=this.context.createDelay(this.toSeconds(e.maxDelay)),this.delayTime=new t.Param({param:this._delayNode.delayTime,units:t.Type.Time,value:e.delayTime}),this._readOnly("delayTime")},t.extend(t.Delay),t.Delay.defaults={maxDelay:1,delayTime:0},t.Delay.prototype.dispose=function(){return t.Param.prototype.dispose.call(this),this._delayNode.disconnect(),this._delayNode=null,this._writable("delayTime"),this.delayTime=null,this},t.Delay}),e(function(t){return t.Effect=function(){t.call(this);var e=this.optionsObject(arguments,["wet"],t.Effect.defaults);this._dryWet=new t.CrossFade(e.wet),this.wet=this._dryWet.fade,this.effectSend=this.context.createGain(),this.effectReturn=this.context.createGain(),this.input.connect(this._dryWet.a),this.input.connect(this.effectSend),this.effectReturn.connect(this._dryWet.b),this._dryWet.connect(this.output),this._readOnly(["wet"])},t.extend(t.Effect),t.Effect.defaults={wet:1},t.Effect.prototype.connectEffect=function(t){return this.effectSend.chain(t,this.effectReturn),this},t.Effect.prototype.dispose=function(){return t.prototype.dispose.call(this),this._dryWet.dispose(),this._dryWet=null,this.effectSend.disconnect(),this.effectSend=null,this.effectReturn.disconnect(),this.effectReturn=null,this._writable(["wet"]),this.wet=null,this},t.Effect}),e(function(t){return t.AutoFilter=function(){var e=this.optionsObject(arguments,["frequency","baseFrequency","octaves"],t.AutoFilter.defaults);t.Effect.call(this,e),this._lfo=new t.LFO({frequency:e.frequency,amplitude:e.depth}),this.depth=this._lfo.amplitude,this.frequency=this._lfo.frequency,this.filter=new t.Filter(e.filter),this._octaves=0,this.connectEffect(this.filter),this._lfo.connect(this.filter.frequency),this.type=e.type,this._readOnly(["frequency","depth"]),this.octaves=e.octaves,this.baseFrequency=e.baseFrequency},t.extend(t.AutoFilter,t.Effect),t.AutoFilter.defaults={frequency:1,type:"sine",depth:1,baseFrequency:200,octaves:2.6,filter:{type:"lowpass",rolloff:-12,Q:1}},t.AutoFilter.prototype.start=function(t){return this._lfo.start(t),this},t.AutoFilter.prototype.stop=function(t){return this._lfo.stop(t),this},t.AutoFilter.prototype.sync=function(t){return this._lfo.sync(t),this},t.AutoFilter.prototype.unsync=function(){return this._lfo.unsync(),this},Object.defineProperty(t.AutoFilter.prototype,"type",{get:function(){return this._lfo.type},set:function(t){this._lfo.type=t}}),Object.defineProperty(t.AutoFilter.prototype,"baseFrequency",{get:function(){return this._lfo.min},set:function(t){this._lfo.min=this.toFrequency(t)}}),Object.defineProperty(t.AutoFilter.prototype,"octaves",{get:function(){return this._octaves},set:function(t){this._octaves=t,this._lfo.max=this.baseFrequency*Math.pow(2,t)}}),t.AutoFilter.prototype.dispose=function(){return t.Effect.prototype.dispose.call(this),this._lfo.dispose(),this._lfo=null,this.filter.dispose(),this.filter=null,this._writable(["frequency","depth"]),this.frequency=null,this.depth=null,this},t.AutoFilter}),e(function(t){return t.AutoPanner=function(){var e=this.optionsObject(arguments,["frequency"],t.AutoPanner.defaults);t.Effect.call(this,e),this._lfo=new t.LFO({frequency:e.frequency,amplitude:e.depth,min:0,max:1}),this.depth=this._lfo.amplitude,this._panner=new t.Panner,this.frequency=this._lfo.frequency,this.connectEffect(this._panner),this._lfo.connect(this._panner.pan),this.type=e.type,this._readOnly(["depth","frequency"])},t.extend(t.AutoPanner,t.Effect),t.AutoPanner.defaults={frequency:1,type:"sine",depth:1},t.AutoPanner.prototype.start=function(t){return this._lfo.start(t),this},t.AutoPanner.prototype.stop=function(t){return this._lfo.stop(t),this},t.AutoPanner.prototype.sync=function(t){return this._lfo.sync(t),this},t.AutoPanner.prototype.unsync=function(){return this._lfo.unsync(),this},Object.defineProperty(t.AutoPanner.prototype,"type",{get:function(){return this._lfo.type},set:function(t){this._lfo.type=t}}),t.AutoPanner.prototype.dispose=function(){return t.Effect.prototype.dispose.call(this),this._lfo.dispose(),this._lfo=null,this._panner.dispose(),this._panner=null,this._writable(["depth","frequency"]),this.frequency=null,this.depth=null,this},t.AutoPanner}),e(function(t){return t.AutoWah=function(){var e=this.optionsObject(arguments,["baseFrequency","octaves","sensitivity"],t.AutoWah.defaults);t.Effect.call(this,e),this.follower=new t.Follower(e.follower),this._sweepRange=new t.ScaleExp(0,1,.5),this._baseFrequency=e.baseFrequency,this._octaves=e.octaves,this._inputBoost=this.context.createGain(),this._bandpass=new t.Filter({rolloff:-48,frequency:0,Q:e.Q}),this._peaking=new t.Filter(0,"peaking"),this._peaking.gain.value=e.gain,this.gain=this._peaking.gain,this.Q=this._bandpass.Q,this.effectSend.chain(this._inputBoost,this.follower,this._sweepRange),this._sweepRange.connect(this._bandpass.frequency),this._sweepRange.connect(this._peaking.frequency),this.effectSend.chain(this._bandpass,this._peaking,this.effectReturn),this._setSweepRange(),this.sensitivity=e.sensitivity,this._readOnly(["gain","Q"])},t.extend(t.AutoWah,t.Effect),t.AutoWah.defaults={baseFrequency:100,octaves:6,sensitivity:0,Q:2,gain:2,follower:{attack:.3,release:.5}},Object.defineProperty(t.AutoWah.prototype,"octaves",{get:function(){return this._octaves},set:function(t){this._octaves=t,this._setSweepRange()}}),Object.defineProperty(t.AutoWah.prototype,"baseFrequency",{get:function(){return this._baseFrequency},set:function(t){this._baseFrequency=t,this._setSweepRange()}}),Object.defineProperty(t.AutoWah.prototype,"sensitivity",{get:function(){return this.gainToDb(1/this._inputBoost.gain.value)},set:function(t){this._inputBoost.gain.value=1/this.dbToGain(t)}}),t.AutoWah.prototype._setSweepRange=function(){this._sweepRange.min=this._baseFrequency,this._sweepRange.max=Math.min(this._baseFrequency*Math.pow(2,this._octaves),this.context.sampleRate/2)},t.AutoWah.prototype.dispose=function(){return t.Effect.prototype.dispose.call(this),this.follower.dispose(),this.follower=null,this._sweepRange.dispose(),this._sweepRange=null,this._bandpass.dispose(),this._bandpass=null,this._peaking.dispose(),this._peaking=null,this._inputBoost.disconnect(),this._inputBoost=null,this._writable(["gain","Q"]),this.gain=null,this.Q=null,this},t.AutoWah}),e(function(t){return t.BitCrusher=function(){var e,i=this.optionsObject(arguments,["bits"],t.BitCrusher.defaults);t.Effect.call(this,i),e=1/Math.pow(2,i.bits-1),this._subtract=new t.Subtract,this._modulo=new t.Modulo(e),this._bits=i.bits,this.effectSend.fan(this._subtract,this._modulo),this._modulo.connect(this._subtract,0,1),this._subtract.connect(this.effectReturn)},t.extend(t.BitCrusher,t.Effect),t.BitCrusher.defaults={bits:4},Object.defineProperty(t.BitCrusher.prototype,"bits",{get:function(){return this._bits},set:function(t){this._bits=t;var e=1/Math.pow(2,t-1);this._modulo.value=e}}),t.BitCrusher.prototype.dispose=function(){return t.Effect.prototype.dispose.call(this),this._subtract.dispose(),this._subtract=null,this._modulo.dispose(),this._modulo=null,this},t.BitCrusher}),e(function(t){return t.Chebyshev=function(){var e=this.optionsObject(arguments,["order"],t.Chebyshev.defaults);t.Effect.call(this,e),this._shaper=new t.WaveShaper(4096),this._order=e.order,this.connectEffect(this._shaper),this.order=e.order,this.oversample=e.oversample},t.extend(t.Chebyshev,t.Effect),t.Chebyshev.defaults={order:1,oversample:"none"},t.Chebyshev.prototype._getCoefficient=function(t,e,i){return i.hasOwnProperty(e)?i[e]:(0===e?i[e]=0:1===e?i[e]=t:i[e]=2*t*this._getCoefficient(t,e-1,i)-this._getCoefficient(t,e-2,i),i[e])},Object.defineProperty(t.Chebyshev.prototype,"order",{get:function(){return this._order},set:function(t){var e,i,s,n;for(this._order=t,e=new Array(4096),i=e.length,s=0;i>s;++s)n=2*s/i-1,0===n?e[s]=0:e[s]=this._getCoefficient(n,t,{});this._shaper.curve=e}}),Object.defineProperty(t.Chebyshev.prototype,"oversample",{get:function(){return this._shaper.oversample},set:function(t){this._shaper.oversample=t}}),t.Chebyshev.prototype.dispose=function(){return t.Effect.prototype.dispose.call(this),this._shaper.dispose(),this._shaper=null,this},t.Chebyshev}),e(function(t){return t.StereoEffect=function(){t.call(this);var e=this.optionsObject(arguments,["wet"],t.Effect.defaults);this._dryWet=new t.CrossFade(e.wet),this.wet=this._dryWet.fade,this._split=new t.Split,this.effectSendL=this._split.left,this.effectSendR=this._split.right,this._merge=new t.Merge,this.effectReturnL=this._merge.left,this.effectReturnR=this._merge.right,this.input.connect(this._split),this.input.connect(this._dryWet,0,0),this._merge.connect(this._dryWet,0,1),this._dryWet.connect(this.output),this._readOnly(["wet"])},t.extend(t.StereoEffect,t.Effect),t.StereoEffect.prototype.dispose=function(){return t.prototype.dispose.call(this),this._dryWet.dispose(),this._dryWet=null,this._split.dispose(),this._split=null,this._merge.dispose(),this._merge=null,this.effectSendL=null,this.effectSendR=null,this.effectReturnL=null,this.effectReturnR=null,this._writable(["wet"]),this.wet=null,this},t.StereoEffect}),e(function(t){return t.FeedbackEffect=function(){var e=this.optionsObject(arguments,["feedback"]);e=this.defaultArg(e,t.FeedbackEffect.defaults),t.Effect.call(this,e),this.feedback=new t.Signal(e.feedback,t.Type.NormalRange),this._feedbackGain=this.context.createGain(),this.effectReturn.chain(this._feedbackGain,this.effectSend),this.feedback.connect(this._feedbackGain.gain),this._readOnly(["feedback"])},t.extend(t.FeedbackEffect,t.Effect),t.FeedbackEffect.defaults={feedback:.125},t.FeedbackEffect.prototype.dispose=function(){return t.Effect.prototype.dispose.call(this),this._writable(["feedback"]),this.feedback.dispose(),this.feedback=null,this._feedbackGain.disconnect(),this._feedbackGain=null,this},t.FeedbackEffect}),e(function(t){return t.StereoXFeedbackEffect=function(){var e=this.optionsObject(arguments,["feedback"],t.FeedbackEffect.defaults);t.StereoEffect.call(this,e),this.feedback=new t.Signal(e.feedback,t.Type.NormalRange),this._feedbackLR=this.context.createGain(),this._feedbackRL=this.context.createGain(),this.effectReturnL.chain(this._feedbackLR,this.effectSendR),this.effectReturnR.chain(this._feedbackRL,this.effectSendL),this.feedback.fan(this._feedbackLR.gain,this._feedbackRL.gain),this._readOnly(["feedback"])},t.extend(t.StereoXFeedbackEffect,t.FeedbackEffect),t.StereoXFeedbackEffect.prototype.dispose=function(){return t.StereoEffect.prototype.dispose.call(this),this._writable(["feedback"]),this.feedback.dispose(),this.feedback=null,this._feedbackLR.disconnect(),this._feedbackLR=null,this._feedbackRL.disconnect(),this._feedbackRL=null,this},t.StereoXFeedbackEffect}),e(function(t){return t.Chorus=function(){var e=this.optionsObject(arguments,["frequency","delayTime","depth"],t.Chorus.defaults);t.StereoXFeedbackEffect.call(this,e),this._depth=e.depth,this._delayTime=e.delayTime/1e3,this._lfoL=new t.LFO({frequency:e.frequency,min:0,max:1}),this._lfoR=new t.LFO({frequency:e.frequency,min:0,max:1,phase:180}),this._delayNodeL=this.context.createDelay(),this._delayNodeR=this.context.createDelay(),this.frequency=this._lfoL.frequency,this.effectSendL.chain(this._delayNodeL,this.effectReturnL),this.effectSendR.chain(this._delayNodeR,this.effectReturnR),this.effectSendL.connect(this.effectReturnL),this.effectSendR.connect(this.effectReturnR),this._lfoL.connect(this._delayNodeL.delayTime),this._lfoR.connect(this._delayNodeR.delayTime),this._lfoL.start(),this._lfoR.start(),this._lfoL.frequency.connect(this._lfoR.frequency),this.depth=this._depth,this.frequency.value=e.frequency,this.type=e.type,this._readOnly(["frequency"]),this.spread=e.spread},t.extend(t.Chorus,t.StereoXFeedbackEffect),t.Chorus.defaults={frequency:1.5,delayTime:3.5,depth:.7,feedback:.1,type:"sine",spread:180},Object.defineProperty(t.Chorus.prototype,"depth",{get:function(){return this._depth},set:function(t){this._depth=t;var e=this._delayTime*t;this._lfoL.min=Math.max(this._delayTime-e,0),this._lfoL.max=this._delayTime+e,this._lfoR.min=Math.max(this._delayTime-e,0),this._lfoR.max=this._delayTime+e}}),Object.defineProperty(t.Chorus.prototype,"delayTime",{get:function(){return 1e3*this._delayTime},set:function(t){this._delayTime=t/1e3,this.depth=this._depth}}),Object.defineProperty(t.Chorus.prototype,"type",{get:function(){return this._lfoL.type},set:function(t){this._lfoL.type=t,this._lfoR.type=t}}),Object.defineProperty(t.Chorus.prototype,"spread",{get:function(){return this._lfoR.phase-this._lfoL.phase},set:function(t){this._lfoL.phase=90-t/2,this._lfoR.phase=t/2+90}}),t.Chorus.prototype.dispose=function(){return t.StereoXFeedbackEffect.prototype.dispose.call(this),this._lfoL.dispose(),this._lfoL=null,this._lfoR.dispose(),this._lfoR=null,this._delayNodeL.disconnect(),this._delayNodeL=null,this._delayNodeR.disconnect(),this._delayNodeR=null,this._writable("frequency"),this.frequency=null,this},t.Chorus}),e(function(t){return t.Convolver=function(){var e=this.optionsObject(arguments,["url"],t.Convolver.defaults);t.Effect.call(this,e),this._convolver=this.context.createConvolver(),this._buffer=new t.Buffer(e.url,function(t){this.buffer=t,e.onload()}.bind(this)),this.connectEffect(this._convolver)},t.extend(t.Convolver,t.Effect),t.Convolver.defaults={url:"",onload:t.noOp},Object.defineProperty(t.Convolver.prototype,"buffer",{get:function(){return this._buffer.get()},set:function(t){this._buffer.set(t),this._convolver.buffer=this._buffer.get()}}),t.Convolver.prototype.load=function(t,e){return this._buffer.load(t,function(t){this.buffer=t,e&&e()}.bind(this)),this},t.Convolver.prototype.dispose=function(){return t.Effect.prototype.dispose.call(this),this._convolver.disconnect(),this._convolver=null,this._buffer.dispose(),this._buffer=null,this},t.Convolver}),e(function(t){return t.Distortion=function(){var e=this.optionsObject(arguments,["distortion"],t.Distortion.defaults);t.Effect.call(this,e),this._shaper=new t.WaveShaper(4096),this._distortion=e.distortion,this.connectEffect(this._shaper),this.distortion=e.distortion,this.oversample=e.oversample},t.extend(t.Distortion,t.Effect),t.Distortion.defaults={distortion:.4,oversample:"none"},Object.defineProperty(t.Distortion.prototype,"distortion",{get:function(){return this._distortion},set:function(t){var e,i;this._distortion=t,e=100*t,i=Math.PI/180,this._shaper.setMap(function(t){return Math.abs(t)<.001?0:(3+e)*t*20*i/(Math.PI+e*Math.abs(t))})}}),Object.defineProperty(t.Distortion.prototype,"oversample",{get:function(){return this._shaper.oversample},set:function(t){this._shaper.oversample=t}}),t.Distortion.prototype.dispose=function(){return t.Effect.prototype.dispose.call(this),this._shaper.dispose(),this._shaper=null,this},t.Distortion}),e(function(t){return t.FeedbackDelay=function(){var e=this.optionsObject(arguments,["delayTime","feedback"],t.FeedbackDelay.defaults);t.FeedbackEffect.call(this,e),this.delayTime=new t.Signal(e.delayTime,t.Type.Time),this._delayNode=this.context.createDelay(4),this.connectEffect(this._delayNode),this.delayTime.connect(this._delayNode.delayTime),this._readOnly(["delayTime"])},t.extend(t.FeedbackDelay,t.FeedbackEffect),t.FeedbackDelay.defaults={delayTime:.25},t.FeedbackDelay.prototype.dispose=function(){return t.FeedbackEffect.prototype.dispose.call(this),this.delayTime.dispose(),this._delayNode.disconnect(),this._delayNode=null,this._writable(["delayTime"]),this.delayTime=null,this},t.FeedbackDelay}),e(function(t){var e=[1557/44100,1617/44100,1491/44100,1422/44100,1277/44100,1356/44100,1188/44100,1116/44100],i=[225,556,441,341];return t.Freeverb=function(){var s,n,o,r,a,h,l=this.optionsObject(arguments,["roomSize","dampening"],t.Freeverb.defaults);for(t.StereoEffect.call(this,l),this.roomSize=new t.Signal(l.roomSize,t.Type.NormalRange),this.dampening=new t.Signal(l.dampening,t.Type.Frequency),this._combFilters=[],this._allpassFiltersL=[],this._allpassFiltersR=[],s=0;ss;s++)n=this.context.createBiquadFilter(),n.type="allpass",i.connect(n.Q),e.connect(n.frequency),o[s]=n;return this.connectSeries.apply(this,o),o},Object.defineProperty(t.Phaser.prototype,"octaves",{get:function(){return this._octaves},set:function(t){this._octaves=t;var e=this._baseFrequency*Math.pow(2,t);this._lfoL.max=e,this._lfoR.max=e}}),Object.defineProperty(t.Phaser.prototype,"baseFrequency",{get:function(){return this._baseFrequency},set:function(t){this._baseFrequency=t,this._lfoL.min=t,this._lfoR.min=t,this.octaves=this._octaves}}),t.Phaser.prototype.dispose=function(){var e,i;for(t.StereoEffect.prototype.dispose.call(this),this._writable(["frequency","Q"]),this.Q.dispose(),this.Q=null,this._lfoL.dispose(),this._lfoL=null,this._lfoR.dispose(),this._lfoR=null,e=0;et?(this._lfoA.min=0,this._lfoA.max=this._windowSize,this._lfoB.min=0,this._lfoB.max=this._windowSize,e=this.intervalToFrequencyRatio(t-1)+1):(this._lfoA.min=this._windowSize,this._lfoA.max=0,this._lfoB.min=this._windowSize,this._lfoB.max=0,e=this.intervalToFrequencyRatio(t)-1),this._frequency.value=e*(1.2/this._windowSize)}}),Object.defineProperty(t.PitchShift.prototype,"windowSize",{get:function(){return this._windowSize},set:function(t){this._windowSize=this.toSeconds(t),this.pitch=this._pitch}}),t.PitchShift.prototype.dispose=function(){return t.FeedbackEffect.prototype.dispose.call(this),this._frequency.dispose(),this._frequency=null,this._delayA.disconnect(),this._delayA=null,this._delayB.disconnect(),this._delayB=null,this._lfoA.dispose(),this._lfoA=null,this._lfoB.dispose(),this._lfoB=null,this._crossFade.dispose(),this._crossFade=null,this._crossFadeLFO.dispose(),this._crossFadeLFO=null,this._writable("delayTime"),this._feedbackDelay.dispose(),this._feedbackDelay=null,this.delayTime=null,this},t.PitchShift}),e(function(t){return t.StereoFeedbackEffect=function(){var e=this.optionsObject(arguments,["feedback"],t.FeedbackEffect.defaults);t.StereoEffect.call(this,e),this.feedback=new t.Signal(e.feedback,t.Type.NormalRange),this._feedbackL=this.context.createGain(),this._feedbackR=this.context.createGain(),this.effectReturnL.chain(this._feedbackL,this.effectSendL),this.effectReturnR.chain(this._feedbackR,this.effectSendR),this.feedback.fan(this._feedbackL.gain,this._feedbackR.gain),this._readOnly(["feedback"])},t.extend(t.StereoFeedbackEffect,t.FeedbackEffect),t.StereoFeedbackEffect.prototype.dispose=function(){return t.StereoEffect.prototype.dispose.call(this),this._writable(["feedback"]),this.feedback.dispose(),this.feedback=null,this._feedbackL.disconnect(),this._feedbackL=null,this._feedbackR.disconnect(),this._feedbackR=null,this},t.StereoFeedbackEffect}),e(function(t){return t.StereoWidener=function(){var e=this.optionsObject(arguments,["width"],t.StereoWidener.defaults);t.MidSideEffect.call(this,e),this.width=new t.Signal(e.width,t.Type.NormalRange),this._midMult=new t.Expr("$0 * ($1 * (1 - $2))"),this._sideMult=new t.Expr("$0 * ($1 * $2)"),this._two=new t.Signal(2),this._two.connect(this._midMult,0,1),this.width.connect(this._midMult,0,2),this._two.connect(this._sideMult,0,1),this.width.connect(this._sideMult,0,2),this.midSend.chain(this._midMult,this.midReturn),this.sideSend.chain(this._sideMult,this.sideReturn),this._readOnly(["width"])},t.extend(t.StereoWidener,t.MidSideEffect),t.StereoWidener.defaults={width:.5},t.StereoWidener.prototype.dispose=function(){return t.MidSideEffect.prototype.dispose.call(this),this._writable(["width"]),this.width.dispose(),this.width=null,this._midMult.dispose(),this._midMult=null,this._sideMult.dispose(),this._sideMult=null,this._two.dispose(),this._two=null,this},t.StereoWidener}),e(function(t){return t.Tremolo=function(){var e=this.optionsObject(arguments,["frequency","depth"],t.Tremolo.defaults);t.StereoEffect.call(this,e),this._lfoL=new t.LFO({phase:e.spread,min:1,max:0}),this._lfoR=new t.LFO({phase:e.spread,min:1,max:0}),this._amplitudeL=new t.Gain,this._amplitudeR=new t.Gain,this.frequency=new t.Signal(e.frequency,t.Type.Frequency),this.depth=new t.Signal(e.depth,t.Type.NormalRange),this._readOnly(["frequency","depth"]),this.effectSendL.chain(this._amplitudeL,this.effectReturnL),this.effectSendR.chain(this._amplitudeR,this.effectReturnR),this._lfoL.connect(this._amplitudeL.gain),this._lfoR.connect(this._amplitudeR.gain),this.frequency.fan(this._lfoL.frequency,this._lfoR.frequency),this.depth.fan(this._lfoR.amplitude,this._lfoL.amplitude),this.type=e.type,this.spread=e.spread},t.extend(t.Tremolo,t.StereoEffect),t.Tremolo.defaults={frequency:10,type:"sine",depth:.5,spread:180},t.Tremolo.prototype.start=function(t){return this._lfoL.start(t),this._lfoR.start(t),this},t.Tremolo.prototype.stop=function(t){return this._lfoL.stop(t),this._lfoR.stop(t),this},t.Tremolo.prototype.sync=function(t){return this._lfoL.sync(t),this._lfoR.sync(t),this},t.Tremolo.prototype.unsync=function(){return this._lfoL.unsync(),this._lfoR.unsync(),this},Object.defineProperty(t.Tremolo.prototype,"type",{get:function(){return this._lfoL.type},set:function(t){this._lfoL.type=t,this._lfoR.type=t}}),Object.defineProperty(t.Tremolo.prototype,"spread",{get:function(){return this._lfoR.phase-this._lfoL.phase},set:function(t){this._lfoL.phase=90-t/2,this._lfoR.phase=t/2+90}}),t.Tremolo.prototype.dispose=function(){return t.StereoEffect.prototype.dispose.call(this),this._writable(["frequency","depth"]),this._lfoL.dispose(),this._lfoL=null,this._lfoR.dispose(),this._lfoR=null,this._amplitudeL.dispose(),this._amplitudeL=null,this._amplitudeR.dispose(),this._amplitudeR=null,this.frequency=null,this.depth=null,this},t.Tremolo}),e(function(t){return t.Vibrato=function(){var e=this.optionsObject(arguments,["frequency","depth"],t.Vibrato.defaults);t.Effect.call(this,e),this._delayNode=new t.Delay(0,e.maxDelay),this._lfo=new t.LFO({type:e.type,min:0,max:e.maxDelay,frequency:e.frequency,phase:-90}).start().connect(this._delayNode.delayTime),this.frequency=this._lfo.frequency,this.depth=this._lfo.amplitude,this.depth.value=e.depth,this._readOnly(["frequency","depth"]),this.effectSend.chain(this._delayNode,this.effectReturn)},t.extend(t.Vibrato,t.Effect),t.Vibrato.defaults={maxDelay:.005,frequency:5,depth:.1,type:"sine"},Object.defineProperty(t.Vibrato.prototype,"type",{get:function(){return this._lfo.type},set:function(t){this._lfo.type=t}}),t.Vibrato.prototype.dispose=function(){t.Effect.prototype.dispose.call(this),this._delayNode.dispose(),this._delayNode=null,this._lfo.dispose(),this._lfo=null,this._writable(["frequency","depth"]),this.frequency=null,this.depth=null},t.Vibrato}),e(function(t){return t.Event=function(){var e=this.optionsObject(arguments,["callback","value"],t.Event.defaults);this._loop=e.loop,this.callback=e.callback,this.value=e.value,this._loopStart=this.toTicks(e.loopStart),this._loopEnd=this.toTicks(e.loopEnd),this._state=new t.TimelineState(t.State.Stopped),this._playbackRate=1,this._startOffset=0,this.probability=e.probability,this.humanize=e.humanize,this.mute=e.mute,this.playbackRate=e.playbackRate},t.extend(t.Event),t.Event.defaults={callback:t.noOp,loop:!1,loopEnd:"1m",loopStart:0,playbackRate:1,value:null,probability:1,mute:!1,humanize:!1},t.Event.prototype._rescheduleEvents=function(e){return e=this.defaultArg(e,-1),this._state.forEachFrom(e,function(e){var i,s,n,o;e.state===t.State.Started&&(this.isUndef(e.id)||t.Transport.clear(e.id),s=e.time+Math.round(this.startOffset/this._playbackRate),this._loop?(i=1/0,this.isNumber(this._loop)&&(i=this._loop*this._getLoopDuration()),n=this._state.getEventAfter(s),null!==n&&(i=Math.min(i,n.time-s)),i!==1/0&&(this._state.setStateAtTime(t.State.Stopped,s+i+1),i=t.Time(i,"i")),o=t.Time(this._getLoopDuration(),"i"),e.id=t.Transport.scheduleRepeat(this._tick.bind(this),o,t.TransportTime(s,"i"),i)):e.id=t.Transport.schedule(this._tick.bind(this),s+"i"))}.bind(this)),this},Object.defineProperty(t.Event.prototype,"state",{get:function(){return this._state.getStateAtTime(t.Transport.ticks)}}),Object.defineProperty(t.Event.prototype,"startOffset",{get:function(){return this._startOffset},set:function(t){this._startOffset=t}}),t.Event.prototype.start=function(e){return e=this.toTicks(e),this._state.getStateAtTime(e)===t.State.Stopped&&(this._state.addEvent({state:t.State.Started,time:e,id:void 0}),this._rescheduleEvents(e)),this},t.Event.prototype.stop=function(e){var i,s;return this.cancel(e),e=this.toTicks(e),this._state.getStateAtTime(e)===t.State.Started&&(this._state.setStateAtTime(t.State.Stopped,e),i=this._state.getEventBefore(e),s=e,null!==i&&(s=i.time),this._rescheduleEvents(s)),this},t.Event.prototype.cancel=function(e){return e=this.defaultArg(e,-(1/0)),e=this.toTicks(e),this._state.forEachFrom(e,function(e){t.Transport.clear(e.id)}),this._state.cancel(e),this},t.Event.prototype._tick=function(e){if(!this.mute&&this._state.getStateAtTime(t.Transport.ticks)===t.State.Started){if(this.probability<1&&Math.random()>this.probability)return;if(this.humanize){var i=.02;this.isBoolean(this.humanize)||(i=this.toSeconds(this.humanize)),e+=(2*Math.random()-1)*i}this.callback(e,this.value)}},t.Event.prototype._getLoopDuration=function(){return Math.round((this._loopEnd-this._loopStart)/this._playbackRate)},Object.defineProperty(t.Event.prototype,"loop",{get:function(){return this._loop},set:function(t){this._loop=t,this._rescheduleEvents()}}),Object.defineProperty(t.Event.prototype,"playbackRate",{get:function(){return this._playbackRate},set:function(t){this._playbackRate=t,this._rescheduleEvents()}}),Object.defineProperty(t.Event.prototype,"loopEnd",{get:function(){return t.TransportTime(this._loopEnd,"i").toNotation()},set:function(t){this._loopEnd=this.toTicks(t),this._loop&&this._rescheduleEvents()}}),Object.defineProperty(t.Event.prototype,"loopStart",{get:function(){return t.TransportTime(this._loopStart,"i").toNotation()},set:function(t){this._loopStart=this.toTicks(t),this._loop&&this._rescheduleEvents()}}),Object.defineProperty(t.Event.prototype,"progress",{get:function(){var e,i,s,n;return this._loop?(e=t.Transport.ticks,i=this._state.getEvent(e),null!==i&&i.state===t.State.Started?(s=this._getLoopDuration(),n=(e-i.time)%s,n/s):0):0}}),t.Event.prototype.dispose=function(){this.cancel(),this._state.dispose(),this._state=null,this.callback=null,this.value=null},t.Event}),e(function(t){return t.Loop=function(){var e=this.optionsObject(arguments,["callback","interval"],t.Loop.defaults);this._event=new t.Event({callback:this._tick.bind(this),loop:!0,loopEnd:e.interval,playbackRate:e.playbackRate,probability:e.probability}),this.callback=e.callback,this.iterations=e.iterations},t.extend(t.Loop),t.Loop.defaults={interval:"4n",callback:t.noOp,playbackRate:1,iterations:1/0,probability:!0,mute:!1},t.Loop.prototype.start=function(t){return this._event.start(t),this},t.Loop.prototype.stop=function(t){return this._event.stop(t),this},t.Loop.prototype.cancel=function(t){return this._event.cancel(t),this},t.Loop.prototype._tick=function(t){this.callback(t)},Object.defineProperty(t.Loop.prototype,"state",{get:function(){return this._event.state}}),Object.defineProperty(t.Loop.prototype,"progress",{get:function(){return this._event.progress}}),Object.defineProperty(t.Loop.prototype,"interval",{get:function(){return this._event.loopEnd},set:function(t){this._event.loopEnd=t}}),Object.defineProperty(t.Loop.prototype,"playbackRate",{get:function(){return this._event.playbackRate},set:function(t){this._event.playbackRate=t}}),Object.defineProperty(t.Loop.prototype,"humanize",{get:function(){return this._event.humanize},set:function(t){this._event.humanize=t}}),Object.defineProperty(t.Loop.prototype,"probability",{get:function(){return this._event.probability},set:function(t){this._event.probability=t}}),Object.defineProperty(t.Loop.prototype,"mute",{get:function(){return this._event.mute},set:function(t){this._event.mute=t}}),Object.defineProperty(t.Loop.prototype,"iterations",{get:function(){return this._event.loop===!0?1/0:this._event.loop},set:function(t){t===1/0?this._event.loop=!0:this._event.loop=t}}),t.Loop.prototype.dispose=function(){this._event.dispose(),this._event=null,this.callback=null},t.Loop}),e(function(t){return t.Part=function(){var e,i,s=this.optionsObject(arguments,["callback","events"],t.Part.defaults);if(this._loop=s.loop,this._loopStart=this.toTicks(s.loopStart),this._loopEnd=this.toTicks(s.loopEnd),this._playbackRate=s.playbackRate,this._probability=s.probability,this._humanize=s.humanize,this._startOffset=0,this._state=new t.TimelineState(t.State.Stopped),this._events=[],this.callback=s.callback,this.mute=s.mute,e=this.defaultArg(s.events,[]),!this.isUndef(s.events))for(i=0;i=this._loopStart&&e.startOffset=s&&(e.loop=!1,e.start(t.TransportTime(i,"i"))):e.startOffset>=s&&e.start(t.TransportTime(i,"i"))},Object.defineProperty(t.Part.prototype,"startOffset",{get:function(){return this._startOffset},set:function(t){this._startOffset=t,this._forEach(function(t){t.startOffset+=this._startOffset})}}),t.Part.prototype.stop=function(e){var i=this.toTicks(e);return this._state.cancel(i),this._state.setStateAtTime(t.State.Stopped,i),this._forEach(function(t){t.stop(e)}),this},t.Part.prototype.at=function(e,i){var s,n,o;for(e=t.TransportTime(e),s=t.Time(1,"i").toSeconds(),n=0;n=0;s--)n=this._events[s],n instanceof t.Part?n.remove(e,i):n.startOffset===e&&(this.isUndef(i)||!this.isUndef(i)&&n.value===i)&&(this._events.splice(s,1),n.dispose());return this},t.Part.prototype.removeAll=function(){return this._forEach(function(t){t.dispose()}),this._events=[],this},t.Part.prototype.cancel=function(t){return this._forEach(function(e){e.cancel(t)}),this._state.cancel(t),this},t.Part.prototype._forEach=function(e,i){var s,n;for(i=this.defaultArg(i,this),s=this._events.length-1;s>=0;s--)n=this._events[s],n instanceof t.Part?n._forEach(e,i):e.call(i,n);return this},t.Part.prototype._setAll=function(t,e){this._forEach(function(i){i[t]=e})},t.Part.prototype._tick=function(t,e){this.mute||this.callback(t,e)},t.Part.prototype._testLoopBoundries=function(e){e.startOffset=this._loopEnd?e.cancel():e.state===t.State.Stopped&&this._restartEvent(e)},Object.defineProperty(t.Part.prototype,"probability",{get:function(){return this._probability},set:function(t){this._probability=t,this._setAll("probability",t)}}),Object.defineProperty(t.Part.prototype,"humanize",{get:function(){return this._humanize},set:function(t){this._humanize=t,this._setAll("humanize",t)}}),Object.defineProperty(t.Part.prototype,"loop",{get:function(){return this._loop},set:function(t){this._loop=t,this._forEach(function(e){e._loopStart=this._loopStart,e._loopEnd=this._loopEnd,e.loop=t,this._testLoopBoundries(e)})}}),Object.defineProperty(t.Part.prototype,"loopEnd",{get:function(){return t.TransportTime(this._loopEnd,"i").toNotation()},set:function(t){this._loopEnd=this.toTicks(t),this._loop&&this._forEach(function(t){t.loopEnd=this.loopEnd,this._testLoopBoundries(t)})}}),Object.defineProperty(t.Part.prototype,"loopStart",{get:function(){return t.TransportTime(this._loopStart,"i").toNotation()},set:function(t){this._loopStart=this.toTicks(t),this._loop&&this._forEach(function(t){t.loopStart=this.loopStart,this._testLoopBoundries(t)})}}),Object.defineProperty(t.Part.prototype,"playbackRate",{get:function(){return this._playbackRate},set:function(t){this._playbackRate=t,this._setAll("playbackRate",t)}}),Object.defineProperty(t.Part.prototype,"length",{get:function(){return this._events.length}}),t.Part.prototype.dispose=function(){return this.removeAll(),this._state.dispose(),this._state=null,this.callback=null,this._events=null,this},t.Part}),e(function(t){return t.Pattern=function(){var e=this.optionsObject(arguments,["callback","values","pattern"],t.Pattern.defaults);t.Loop.call(this,e),this._pattern=new t.CtrlPattern({values:e.values,type:e.pattern,index:e.index})},t.extend(t.Pattern,t.Loop),t.Pattern.defaults={pattern:t.CtrlPattern.Type.Up,values:[]},t.Pattern.prototype._tick=function(t){this.callback(t,this._pattern.value),this._pattern.next()},Object.defineProperty(t.Pattern.prototype,"index",{get:function(){return this._pattern.index},set:function(t){this._pattern.index=t}}),Object.defineProperty(t.Pattern.prototype,"values",{get:function(){return this._pattern.values},set:function(t){this._pattern.values=t}}),Object.defineProperty(t.Pattern.prototype,"value",{get:function(){return this._pattern.value}}),Object.defineProperty(t.Pattern.prototype,"pattern",{get:function(){return this._pattern.type},set:function(t){this._pattern.type=t}}),t.Pattern.prototype.dispose=function(){t.Loop.prototype.dispose.call(this),this._pattern.dispose(),this._pattern=null},t.Pattern}),e(function(t){return t.Sequence=function(){var e,i=this.optionsObject(arguments,["callback","events","subdivision"],t.Sequence.defaults),s=i.events;if(delete i.events,t.Part.call(this,i),this._subdivision=this.toTicks(i.subdivision),this.isUndef(i.loopEnd)&&!this.isUndef(s)&&(this._loopEnd=s.length*this._subdivision),this._loop=!0,!this.isUndef(s))for(e=0;et?-1:1}),this._sawtooth.chain(this._thresh,this.output),this.width.chain(this._widthGate,this._thresh),this._readOnly(["width","frequency","detune"])},t.extend(t.PulseOscillator,t.Oscillator),t.PulseOscillator.defaults={frequency:440,detune:0,phase:0,width:.2},t.PulseOscillator.prototype._start=function(t){t=this.toSeconds(t),this._sawtooth.start(t),this._widthGate.gain.setValueAtTime(1,t)},t.PulseOscillator.prototype._stop=function(t){t=this.toSeconds(t),this._sawtooth.stop(t),this._widthGate.gain.setValueAtTime(0,t)},Object.defineProperty(t.PulseOscillator.prototype,"phase",{get:function(){return this._sawtooth.phase},set:function(t){this._sawtooth.phase=t}}),Object.defineProperty(t.PulseOscillator.prototype,"type",{get:function(){return"pulse"}}),Object.defineProperty(t.PulseOscillator.prototype,"partials",{get:function(){return[]}}),t.PulseOscillator.prototype.dispose=function(){return t.Source.prototype.dispose.call(this),this._sawtooth.dispose(),this._sawtooth=null,this._writable(["width","frequency","detune"]),this.width.dispose(),this.width=null,this._widthGate.disconnect(),this._widthGate=null,this._widthGate=null,this._thresh.disconnect(),this._thresh=null,this.frequency=null,this.detune=null,this},t.PulseOscillator}),e(function(t){return t.PWMOscillator=function(){var e=this.optionsObject(arguments,["frequency","modulationFrequency"],t.PWMOscillator.defaults);t.Source.call(this,e),this._pulse=new t.PulseOscillator(e.modulationFrequency),this._pulse._sawtooth.type="sine",this._modulator=new t.Oscillator({frequency:e.frequency,detune:e.detune,phase:e.phase}),this._scale=new t.Multiply(2),this.frequency=this._modulator.frequency,this.detune=this._modulator.detune,this.modulationFrequency=this._pulse.frequency,this._modulator.chain(this._scale,this._pulse.width),this._pulse.connect(this.output),this._readOnly(["modulationFrequency","frequency","detune"])},t.extend(t.PWMOscillator,t.Oscillator),t.PWMOscillator.defaults={frequency:440,detune:0,phase:0,modulationFrequency:.4},t.PWMOscillator.prototype._start=function(t){t=this.toSeconds(t),this._modulator.start(t),this._pulse.start(t)},t.PWMOscillator.prototype._stop=function(t){t=this.toSeconds(t),this._modulator.stop(t),this._pulse.stop(t)},Object.defineProperty(t.PWMOscillator.prototype,"type",{get:function(){return"pwm"}}),Object.defineProperty(t.PWMOscillator.prototype,"partials",{get:function(){return[]}}),Object.defineProperty(t.PWMOscillator.prototype,"phase",{get:function(){return this._modulator.phase},set:function(t){this._modulator.phase=t}}),t.PWMOscillator.prototype.dispose=function(){return t.Source.prototype.dispose.call(this),this._pulse.dispose(),this._pulse=null,this._scale.dispose(),this._scale=null,this._modulator.dispose(),this._modulator=null,this._writable(["modulationFrequency","frequency","detune"]),this.frequency=null,this.detune=null,this.modulationFrequency=null,this},t.PWMOscillator}),e(function(t){return t.FMOscillator=function(){var e=this.optionsObject(arguments,["frequency","type","modulationType"],t.FMOscillator.defaults);t.Source.call(this,e),this._carrier=new t.Oscillator(e.frequency,e.type),this.frequency=new t.Signal(e.frequency,t.Type.Frequency),this.detune=this._carrier.detune,this.detune.value=e.detune,this.modulationIndex=new t.Multiply(e.modulationIndex),this.modulationIndex.units=t.Type.Positive,this._modulator=new t.Oscillator(e.frequency,e.modulationType),this.harmonicity=new t.Multiply(e.harmonicity),this.harmonicity.units=t.Type.Positive,this._modulationNode=new t.Gain(0), +this.frequency.connect(this._carrier.frequency),this.frequency.chain(this.harmonicity,this._modulator.frequency),this.frequency.chain(this.modulationIndex,this._modulationNode),this._modulator.connect(this._modulationNode.gain),this._modulationNode.connect(this._carrier.frequency),this._carrier.connect(this.output),this.detune.connect(this._modulator.detune),this.phase=e.phase,this._readOnly(["modulationIndex","frequency","detune","harmonicity"])},t.extend(t.FMOscillator,t.Oscillator),t.FMOscillator.defaults={frequency:440,detune:0,phase:0,modulationIndex:2,modulationType:"square",harmonicity:1},t.FMOscillator.prototype._start=function(t){t=this.toSeconds(t),this._modulator.start(t),this._carrier.start(t)},t.FMOscillator.prototype._stop=function(t){t=this.toSeconds(t),this._modulator.stop(t),this._carrier.stop(t)},Object.defineProperty(t.FMOscillator.prototype,"type",{get:function(){return this._carrier.type},set:function(t){this._carrier.type=t}}),Object.defineProperty(t.FMOscillator.prototype,"modulationType",{get:function(){return this._modulator.type},set:function(t){this._modulator.type=t}}),Object.defineProperty(t.FMOscillator.prototype,"phase",{get:function(){return this._carrier.phase},set:function(t){this._carrier.phase=t,this._modulator.phase=t}}),Object.defineProperty(t.FMOscillator.prototype,"partials",{get:function(){return this._carrier.partials},set:function(t){this._carrier.partials=t}}),t.FMOscillator.prototype.dispose=function(){return t.Source.prototype.dispose.call(this),this._writable(["modulationIndex","frequency","detune","harmonicity"]),this.frequency.dispose(),this.frequency=null,this.detune=null,this.harmonicity.dispose(),this.harmonicity=null,this._carrier.dispose(),this._carrier=null,this._modulator.dispose(),this._modulator=null,this._modulationNode.dispose(),this._modulationNode=null,this.modulationIndex.dispose(),this.modulationIndex=null,this},t.FMOscillator}),e(function(t){return t.AMOscillator=function(){var e=this.optionsObject(arguments,["frequency","type","modulationType"],t.AMOscillator.defaults);t.Source.call(this,e),this._carrier=new t.Oscillator(e.frequency,e.type),this.frequency=this._carrier.frequency,this.detune=this._carrier.detune,this.detune.value=e.detune,this._modulator=new t.Oscillator(e.frequency,e.modulationType),this._modulationScale=new t.AudioToGain,this.harmonicity=new t.Multiply(e.harmonicity),this.harmonicity.units=t.Type.Positive,this._modulationNode=new t.Gain(0),this.frequency.chain(this.harmonicity,this._modulator.frequency),this.detune.connect(this._modulator.detune),this._modulator.chain(this._modulationScale,this._modulationNode.gain),this._carrier.chain(this._modulationNode,this.output),this.phase=e.phase,this._readOnly(["frequency","detune","harmonicity"])},t.extend(t.AMOscillator,t.Oscillator),t.AMOscillator.defaults={frequency:440,detune:0,phase:0,modulationType:"square",harmonicity:1},t.AMOscillator.prototype._start=function(t){t=this.toSeconds(t),this._modulator.start(t),this._carrier.start(t)},t.AMOscillator.prototype._stop=function(t){t=this.toSeconds(t),this._modulator.stop(t),this._carrier.stop(t)},Object.defineProperty(t.AMOscillator.prototype,"type",{get:function(){return this._carrier.type},set:function(t){this._carrier.type=t}}),Object.defineProperty(t.AMOscillator.prototype,"modulationType",{get:function(){return this._modulator.type},set:function(t){this._modulator.type=t}}),Object.defineProperty(t.AMOscillator.prototype,"phase",{get:function(){return this._carrier.phase},set:function(t){this._carrier.phase=t,this._modulator.phase=t}}),Object.defineProperty(t.AMOscillator.prototype,"partials",{get:function(){return this._carrier.partials},set:function(t){this._carrier.partials=t}}),t.AMOscillator.prototype.dispose=function(){return t.Source.prototype.dispose.call(this),this._writable(["frequency","detune","harmonicity"]),this.frequency=null,this.detune=null,this.harmonicity.dispose(),this.harmonicity=null,this._carrier.dispose(),this._carrier=null,this._modulator.dispose(),this._modulator=null,this._modulationNode.dispose(),this._modulationNode=null,this._modulationScale.dispose(),this._modulationScale=null,this},t.AMOscillator}),e(function(t){return t.FatOscillator=function(){var e=this.optionsObject(arguments,["frequency","type","spread"],t.FatOscillator.defaults);t.Source.call(this,e),this.frequency=new t.Signal(e.frequency,t.Type.Frequency),this.detune=new t.Signal(e.detune,t.Type.Cents),this._oscillators=[],this._spread=e.spread,this._type=e.type,this._phase=e.phase,this._partials=this.defaultArg(e.partials,[]),this.count=e.count,this._readOnly(["frequency","detune"])},t.extend(t.FatOscillator,t.Oscillator),t.FatOscillator.defaults={frequency:440,detune:0,phase:0,spread:20,count:3,type:"sawtooth"},t.FatOscillator.prototype._start=function(t){t=this.toSeconds(t),this._forEach(function(e){e.start(t)})},t.FatOscillator.prototype._stop=function(t){t=this.toSeconds(t),this._forEach(function(e){e.stop(t)})},t.FatOscillator.prototype._forEach=function(t){for(var e=0;e1&&(e=-t/2,i=t/(this._oscillators.length-1),this._forEach(function(t,s){t.detune.value=e+i*s}))}}),Object.defineProperty(t.FatOscillator.prototype,"count",{get:function(){return this._oscillators.length},set:function(e){var i,s;if(e=Math.max(e,1),this._oscillators.length!==e){for(this._forEach(function(t){t.dispose()}),this._oscillators=[],i=0;e>i;i++)s=new t.Oscillator,this.type===t.Oscillator.Type.Custom?s.partials=this._partials:s.type=this._type,s.phase=this._phase,s.volume.value=-6-e,this.frequency.connect(s.frequency),this.detune.connect(s.detune),s.connect(this.output),this._oscillators[i]=s;this.spread=this._spread,this.state===t.State.Started&&this._forEach(function(t){t.start()})}}}),Object.defineProperty(t.FatOscillator.prototype,"phase",{get:function(){return this._phase},set:function(t){this._phase=t,this._forEach(function(e){e.phase=t})}}),Object.defineProperty(t.FatOscillator.prototype,"partials",{get:function(){return this._partials},set:function(e){this._partials=e,this._type=t.Oscillator.Type.Custom,this._forEach(function(t){t.partials=e})}}),t.FatOscillator.prototype.dispose=function(){return t.Source.prototype.dispose.call(this),this._writable(["frequency","detune"]),this.frequency.dispose(),this.frequency=null,this.detune.dispose(),this.detune=null,this._forEach(function(t){t.dispose()}),this._oscillators=null,this._partials=null,this},t.FatOscillator}),e(function(t){t.OmniOscillator=function(){var e=this.optionsObject(arguments,["frequency","type"],t.OmniOscillator.defaults);t.Source.call(this,e),this.frequency=new t.Signal(e.frequency,t.Type.Frequency),this.detune=new t.Signal(e.detune,t.Type.Cents),this._sourceType=void 0,this._oscillator=null,this.type=e.type,this._readOnly(["frequency","detune"]),this.set(e)},t.extend(t.OmniOscillator,t.Oscillator),t.OmniOscillator.defaults={frequency:440,detune:0,type:"sine",phase:0};var e={Pulse:"PulseOscillator",PWM:"PWMOscillator",Osc:"Oscillator",FM:"FMOscillator",AM:"AMOscillator",Fat:"FatOscillator"};return t.OmniOscillator.prototype._start=function(t){this._oscillator.start(t)},t.OmniOscillator.prototype._stop=function(t){this._oscillator.stop(t)},Object.defineProperty(t.OmniOscillator.prototype,"type",{get:function(){var t="";return this._sourceType===e.FM?t="fm":this._sourceType===e.AM?t="am":this._sourceType===e.Fat&&(t="fat"),t+this._oscillator.type},set:function(t){"fm"===t.substr(0,2)?(this._createNewOscillator(e.FM),this._oscillator.type=t.substr(2)):"am"===t.substr(0,2)?(this._createNewOscillator(e.AM),this._oscillator.type=t.substr(2)):"fat"===t.substr(0,3)?(this._createNewOscillator(e.Fat),this._oscillator.type=t.substr(3)):"pwm"===t?this._createNewOscillator(e.PWM):"pulse"===t?this._createNewOscillator(e.Pulse):(this._createNewOscillator(e.Osc),this._oscillator.type=t)}}),Object.defineProperty(t.OmniOscillator.prototype,"partials",{get:function(){return this._oscillator.partials},set:function(t){this._oscillator.partials=t}}),t.OmniOscillator.prototype.set=function(e,i){return"type"===e?this.type=i:this.isObject(e)&&e.hasOwnProperty("type")&&(this.type=e.type),t.prototype.set.apply(this,arguments),this},t.OmniOscillator.prototype._createNewOscillator=function(e){var i,s,n;e!==this._sourceType&&(this._sourceType=e,i=t[e],s=this.now()+this.blockTime,null!==this._oscillator&&(n=this._oscillator,n.stop(s),setTimeout(function(){n.dispose(),n=null},1e3*this.blockTime)),this._oscillator=new i,this.frequency.connect(this._oscillator.frequency),this.detune.connect(this._oscillator.detune),this._oscillator.connect(this.output),this.state===t.State.Started&&this._oscillator.start(s))},Object.defineProperty(t.OmniOscillator.prototype,"phase",{get:function(){return this._oscillator.phase},set:function(t){this._oscillator.phase=t}}),Object.defineProperty(t.OmniOscillator.prototype,"width",{get:function(){return this._sourceType===e.Pulse?this._oscillator.width:void 0}}),Object.defineProperty(t.OmniOscillator.prototype,"count",{get:function(){return this._sourceType===e.Fat?this._oscillator.count:void 0},set:function(t){this._sourceType===e.Fat&&(this._oscillator.count=t)}}),Object.defineProperty(t.OmniOscillator.prototype,"spread",{get:function(){return this._sourceType===e.Fat?this._oscillator.spread:void 0},set:function(t){this._sourceType===e.Fat&&(this._oscillator.spread=t)}}),Object.defineProperty(t.OmniOscillator.prototype,"modulationType",{get:function(){return this._sourceType===e.FM||this._sourceType===e.AM?this._oscillator.modulationType:void 0},set:function(t){(this._sourceType===e.FM||this._sourceType===e.AM)&&(this._oscillator.modulationType=t)}}),Object.defineProperty(t.OmniOscillator.prototype,"modulationIndex",{get:function(){return this._sourceType===e.FM?this._oscillator.modulationIndex:void 0}}),Object.defineProperty(t.OmniOscillator.prototype,"harmonicity",{get:function(){return this._sourceType===e.FM||this._sourceType===e.AM?this._oscillator.harmonicity:void 0}}),Object.defineProperty(t.OmniOscillator.prototype,"modulationFrequency",{get:function(){return this._sourceType===e.PWM?this._oscillator.modulationFrequency:void 0}}),t.OmniOscillator.prototype.dispose=function(){return t.Source.prototype.dispose.call(this),this._writable(["frequency","detune"]),this.detune.dispose(),this.detune=null,this.frequency.dispose(),this.frequency=null,this._oscillator.dispose(),this._oscillator=null,this._sourceType=null,this},t.OmniOscillator}),e(function(t){return t.Instrument=function(e){e=this.defaultArg(e,t.Instrument.defaults),this._volume=this.output=new t.Volume(e.volume),this.volume=this._volume.volume,this._readOnly("volume")},t.extend(t.Instrument),t.Instrument.defaults={volume:0},t.Instrument.prototype.triggerAttack=t.noOp,t.Instrument.prototype.triggerRelease=t.noOp,t.Instrument.prototype.triggerAttackRelease=function(t,e,i,s){return i=this.toSeconds(i),e=this.toSeconds(e),this.triggerAttack(t,i,s),this.triggerRelease(i+e),this},t.Instrument.prototype.dispose=function(){return t.prototype.dispose.call(this),this._volume.dispose(),this._volume=null,this._writable(["volume"]),this.volume=null,this},t.Instrument}),e(function(t){return t.Monophonic=function(e){e=this.defaultArg(e,t.Monophonic.defaults),t.Instrument.call(this,e),this.portamento=e.portamento},t.extend(t.Monophonic,t.Instrument),t.Monophonic.defaults={portamento:0},t.Monophonic.prototype.triggerAttack=function(t,e,i){return e=this.toSeconds(e),this._triggerEnvelopeAttack(e,i),this.setNote(t,e),this},t.Monophonic.prototype.triggerRelease=function(t){return this._triggerEnvelopeRelease(t),this},t.Monophonic.prototype._triggerEnvelopeAttack=function(){},t.Monophonic.prototype._triggerEnvelopeRelease=function(){},t.Monophonic.prototype.setNote=function(t,e){var i,s;return e=this.toSeconds(e),this.portamento>0?(i=this.frequency.value,this.frequency.setValueAtTime(i,e),s=this.toSeconds(this.portamento),this.frequency.exponentialRampToValueAtTime(t,e+s)):this.frequency.setValueAtTime(t,e),this},t.Monophonic}),e(function(t){return t.Synth=function(e){e=this.defaultArg(e,t.Synth.defaults),t.Monophonic.call(this,e),this.oscillator=new t.OmniOscillator(e.oscillator),this.frequency=this.oscillator.frequency,this.detune=this.oscillator.detune,this.envelope=new t.AmplitudeEnvelope(e.envelope),this.oscillator.chain(this.envelope,this.output),this.oscillator.start(),this._readOnly(["oscillator","frequency","detune","envelope"])},t.extend(t.Synth,t.Monophonic),t.Synth.defaults={oscillator:{type:"triangle"},envelope:{attack:.005,decay:.1,sustain:.3,release:1}},t.Synth.prototype._triggerEnvelopeAttack=function(t,e){return this.envelope.triggerAttack(t,e),this},t.Synth.prototype._triggerEnvelopeRelease=function(t){return this.envelope.triggerRelease(t),this},t.Synth.prototype.dispose=function(){return t.Monophonic.prototype.dispose.call(this),this._writable(["oscillator","frequency","detune","envelope"]),this.oscillator.dispose(),this.oscillator=null,this.envelope.dispose(),this.envelope=null,this.frequency=null,this.detune=null,this},t.Synth}),e(function(t){return t.AMSynth=function(e){e=this.defaultArg(e,t.AMSynth.defaults),t.Monophonic.call(this,e),this._carrier=new t.Synth,this._carrier.volume.value=-10,this.oscillator=this._carrier.oscillator,this.envelope=this._carrier.envelope.set(e.envelope),this._modulator=new t.Synth,this._modulator.volume.value=-10,this.modulation=this._modulator.oscillator.set(e.modulation),this.modulationEnvelope=this._modulator.envelope.set(e.modulationEnvelope),this.frequency=new t.Signal(440,t.Type.Frequency),this.detune=new t.Signal(e.detune,t.Type.Cents),this.harmonicity=new t.Multiply(e.harmonicity),this.harmonicity.units=t.Type.Positive,this._modulationScale=new t.AudioToGain,this._modulationNode=this.context.createGain(),this.frequency.connect(this._carrier.frequency),this.frequency.chain(this.harmonicity,this._modulator.frequency),this.detune.fan(this._carrier.detune,this._modulator.detune),this._modulator.chain(this._modulationScale,this._modulationNode.gain),this._carrier.chain(this._modulationNode,this.output),this._readOnly(["frequency","harmonicity","oscillator","envelope","modulation","modulationEnvelope","detune"])},t.extend(t.AMSynth,t.Monophonic),t.AMSynth.defaults={harmonicity:3,detune:0,oscillator:{type:"sine"},envelope:{attack:.01,decay:.01,sustain:1,release:.5},moduation:{type:"square"},modulationEnvelope:{attack:.5,decay:0,sustain:1,release:.5}},t.AMSynth.prototype._triggerEnvelopeAttack=function(t,e){return t=this.toSeconds(t),this.envelope.triggerAttack(t,e),this.modulationEnvelope.triggerAttack(t,e),this},t.AMSynth.prototype._triggerEnvelopeRelease=function(t){return this.envelope.triggerRelease(t),this.modulationEnvelope.triggerRelease(t),this},t.AMSynth.prototype.dispose=function(){return t.Monophonic.prototype.dispose.call(this),this._writable(["frequency","harmonicity","oscillator","envelope","modulation","modulationEnvelope","detune"]),this._carrier.dispose(),this._carrier=null,this._modulator.dispose(),this._modulator=null,this.frequency.dispose(),this.frequency=null,this.detune.dispose(),this.detune=null,this.harmonicity.dispose(),this.harmonicity=null,this._modulationScale.dispose(),this._modulationScale=null,this._modulationNode.disconnect(),this._modulationNode=null,this.oscillator=null,this.envelope=null,this.modulationEnvelope=null,this.modulation=null,this},t.AMSynth}),e(function(t){return t.MonoSynth=function(e){e=this.defaultArg(e,t.MonoSynth.defaults),t.Monophonic.call(this,e),this.oscillator=new t.OmniOscillator(e.oscillator),this.frequency=this.oscillator.frequency,this.detune=this.oscillator.detune,this.filter=new t.Filter(e.filter),this.filterEnvelope=new t.FrequencyEnvelope(e.filterEnvelope),this.envelope=new t.AmplitudeEnvelope(e.envelope),this.oscillator.chain(this.filter,this.envelope,this.output),this.oscillator.start(),this.filterEnvelope.connect(this.filter.frequency),this._readOnly(["oscillator","frequency","detune","filter","filterEnvelope","envelope"])},t.extend(t.MonoSynth,t.Monophonic),t.MonoSynth.defaults={frequency:"C4",detune:0,oscillator:{type:"square"},filter:{Q:6,type:"lowpass",rolloff:-24},envelope:{attack:.005,decay:.1,sustain:.9,release:1},filterEnvelope:{attack:.06,decay:.2,sustain:.5,release:2,baseFrequency:200,octaves:7,exponent:2}},t.MonoSynth.prototype._triggerEnvelopeAttack=function(t,e){return this.envelope.triggerAttack(t,e),this.filterEnvelope.triggerAttack(t),this},t.MonoSynth.prototype._triggerEnvelopeRelease=function(t){return this.envelope.triggerRelease(t),this.filterEnvelope.triggerRelease(t),this},t.MonoSynth.prototype.dispose=function(){return t.Monophonic.prototype.dispose.call(this),this._writable(["oscillator","frequency","detune","filter","filterEnvelope","envelope"]),this.oscillator.dispose(),this.oscillator=null,this.envelope.dispose(),this.envelope=null,this.filterEnvelope.dispose(),this.filterEnvelope=null,this.filter.dispose(),this.filter=null,this.frequency=null,this.detune=null,this},t.MonoSynth}),e(function(t){return t.DuoSynth=function(e){e=this.defaultArg(e,t.DuoSynth.defaults),t.Monophonic.call(this,e),this.voice0=new t.MonoSynth(e.voice0),this.voice0.volume.value=-10,this.voice1=new t.MonoSynth(e.voice1),this.voice1.volume.value=-10,this._vibrato=new t.LFO(e.vibratoRate,-50,50),this._vibrato.start(),this.vibratoRate=this._vibrato.frequency,this._vibratoGain=this.context.createGain(),this.vibratoAmount=new t.Param({param:this._vibratoGain.gain,units:t.Type.Positive,value:e.vibratoAmount}),this.frequency=new t.Signal(440,t.Type.Frequency),this.harmonicity=new t.Multiply(e.harmonicity),this.harmonicity.units=t.Type.Positive,this.frequency.connect(this.voice0.frequency),this.frequency.chain(this.harmonicity,this.voice1.frequency),this._vibrato.connect(this._vibratoGain),this._vibratoGain.fan(this.voice0.detune,this.voice1.detune),this.voice0.connect(this.output),this.voice1.connect(this.output),this._readOnly(["voice0","voice1","frequency","vibratoAmount","vibratoRate"])},t.extend(t.DuoSynth,t.Monophonic),t.DuoSynth.defaults={vibratoAmount:.5,vibratoRate:5,harmonicity:1.5,voice0:{volume:-10,portamento:0,oscillator:{type:"sine"},filterEnvelope:{attack:.01,decay:0,sustain:1,release:.5},envelope:{attack:.01,decay:0,sustain:1,release:.5}},voice1:{volume:-10,portamento:0,oscillator:{type:"sine"},filterEnvelope:{attack:.01,decay:0,sustain:1,release:.5},envelope:{attack:.01,decay:0,sustain:1,release:.5}}},t.DuoSynth.prototype._triggerEnvelopeAttack=function(t,e){return t=this.toSeconds(t),this.voice0.envelope.triggerAttack(t,e),this.voice1.envelope.triggerAttack(t,e),this.voice0.filterEnvelope.triggerAttack(t),this.voice1.filterEnvelope.triggerAttack(t),this},t.DuoSynth.prototype._triggerEnvelopeRelease=function(t){return this.voice0.triggerRelease(t),this.voice1.triggerRelease(t),this},t.DuoSynth.prototype.dispose=function(){return t.Monophonic.prototype.dispose.call(this),this._writable(["voice0","voice1","frequency","vibratoAmount","vibratoRate"]),this.voice0.dispose(),this.voice0=null,this.voice1.dispose(),this.voice1=null,this.frequency.dispose(),this.frequency=null,this._vibrato.dispose(),this._vibrato=null,this._vibratoGain.disconnect(),this._vibratoGain=null,this.harmonicity.dispose(),this.harmonicity=null,this.vibratoAmount.dispose(),this.vibratoAmount=null,this.vibratoRate=null,this},t.DuoSynth}),e(function(t){return t.FMSynth=function(e){e=this.defaultArg(e,t.FMSynth.defaults),t.Monophonic.call(this,e),this._carrier=new t.Synth(e.carrier),this._carrier.volume.value=-10,this.oscillator=this._carrier.oscillator,this.envelope=this._carrier.envelope.set(e.envelope),this._modulator=new t.Synth(e.modulator),this._modulator.volume.value=-10,this.modulation=this._modulator.oscillator.set(e.modulation),this.modulationEnvelope=this._modulator.envelope.set(e.modulationEnvelope),this.frequency=new t.Signal(440,t.Type.Frequency),this.detune=new t.Signal(e.detune,t.Type.Cents),this.harmonicity=new t.Multiply(e.harmonicity),this.harmonicity.units=t.Type.Positive,this.modulationIndex=new t.Multiply(e.modulationIndex),this.modulationIndex.units=t.Type.Positive,this._modulationNode=this.context.createGain(),this.frequency.connect(this._carrier.frequency),this.frequency.chain(this.harmonicity,this._modulator.frequency),this.frequency.chain(this.modulationIndex,this._modulationNode),this.detune.fan(this._carrier.detune,this._modulator.detune),this._modulator.connect(this._modulationNode.gain),this._modulationNode.gain.value=0,this._modulationNode.connect(this._carrier.frequency),this._carrier.connect(this.output),this._readOnly(["frequency","harmonicity","modulationIndex","oscillator","envelope","modulation","modulationEnvelope","detune"])},t.extend(t.FMSynth,t.Monophonic),t.FMSynth.defaults={harmonicity:3,modulationIndex:10,detune:0,oscillator:{type:"sine"},envelope:{attack:.01,decay:.01,sustain:1,release:.5},moduation:{type:"square"},modulationEnvelope:{attack:.5,decay:0,sustain:1,release:.5}},t.FMSynth.prototype._triggerEnvelopeAttack=function(t,e){return t=this.toSeconds(t),this.envelope.triggerAttack(t,e),this.modulationEnvelope.triggerAttack(t),this},t.FMSynth.prototype._triggerEnvelopeRelease=function(t){return t=this.toSeconds(t),this.envelope.triggerRelease(t),this.modulationEnvelope.triggerRelease(t),this},t.FMSynth.prototype.dispose=function(){return t.Monophonic.prototype.dispose.call(this),this._writable(["frequency","harmonicity","modulationIndex","oscillator","envelope","modulation","modulationEnvelope","detune"]),this._carrier.dispose(),this._carrier=null,this._modulator.dispose(),this._modulator=null,this.frequency.dispose(),this.frequency=null,this.detune.dispose(),this.detune=null,this.modulationIndex.dispose(),this.modulationIndex=null,this.harmonicity.dispose(),this.harmonicity=null,this._modulationNode.disconnect(),this._modulationNode=null,this.oscillator=null,this.envelope=null,this.modulationEnvelope=null,this.modulation=null,this},t.FMSynth}),e(function(t){return t.MembraneSynth=function(e){e=this.defaultArg(e,t.MembraneSynth.defaults),t.Instrument.call(this,e),this.oscillator=new t.Oscillator(e.oscillator).start(),this.envelope=new t.AmplitudeEnvelope(e.envelope),this.octaves=e.octaves,this.pitchDecay=e.pitchDecay,this.oscillator.chain(this.envelope,this.output),this._readOnly(["oscillator","envelope"])},t.extend(t.MembraneSynth,t.Instrument),t.MembraneSynth.defaults={pitchDecay:.05,octaves:10,oscillator:{type:"sine"},envelope:{attack:.001,decay:.4,sustain:.01,release:1.4,attackCurve:"exponential"}},t.MembraneSynth.prototype.triggerAttack=function(t,e,i){e=this.toSeconds(e),t=this.toFrequency(t);var s=t*this.octaves;return this.oscillator.frequency.setValueAtTime(s,e),this.oscillator.frequency.exponentialRampToValueAtTime(t,e+this.toSeconds(this.pitchDecay)),this.envelope.triggerAttack(e,i),this},t.MembraneSynth.prototype.triggerRelease=function(t){return this.envelope.triggerRelease(t),this},t.MembraneSynth.prototype.dispose=function(){return t.Instrument.prototype.dispose.call(this),this._writable(["oscillator","envelope"]),this.oscillator.dispose(),this.oscillator=null,this.envelope.dispose(),this.envelope=null,this},t.MembraneSynth}),e(function(t){var e=[1,1.483,1.932,2.546,2.63,3.897];return t.MetalSynth=function(i){var s,n,o;for(i=this.defaultArg(i,t.MetalSynth.defaults),t.Instrument.call(this,i),this.frequency=new t.Signal(i.frequency,t.Type.Frequency),this._oscillators=[],this._freqMultipliers=[],this._amplitue=new t.Gain(0).connect(this.output),this._highpass=new t.Filter({type:"highpass",Q:0}).connect(this._amplitue),this._octaves=i.octaves,this._filterFreqScaler=new t.Scale(i.resonance,7e3),this.envelope=new t.Envelope({attack:i.envelope.attack,attackCurve:"exponential",decay:i.envelope.decay,sustain:0,release:i.envelope.release}).chain(this._filterFreqScaler,this._highpass.frequency),this.envelope.connect(this._amplitue.gain),s=0;sp;p++)f=2*Math.random()-1,s=.99886*s+.0555179*f,r=.99332*r+.0750759*f,a=.969*a+.153852*f,h=.8665*h+.3104856*f,l=.55*l+.5329522*f,u=-.7616*u-.016898*f,i[p]=s+r+a+h+l+u+c+.5362*f,i[p]*=.11,c=.115926*f;return d}(),i=function(){var e,i,s,r,a,h=t.createBuffer(2,o,n);for(e=0;er;r++)a=2*Math.random()-1,i[r]=(s+.02*a)/1.02,s=i[r],i[r]*=3.5;return h}(),s=function(){var e,i,s,r=t.createBuffer(2,o,n);for(e=0;es;s++)i[s]=2*Math.random()-1;return r}()}),t.Noise}),e(function(t){return t.NoiseSynth=function(e){e=this.defaultArg(e,t.NoiseSynth.defaults),t.Instrument.call(this,e),this.noise=new t.Noise,this.envelope=new t.AmplitudeEnvelope(e.envelope),this.noise.chain(this.envelope,this.output),this.noise.start(),this._readOnly(["noise","envelope"])},t.extend(t.NoiseSynth,t.Instrument),t.NoiseSynth.defaults={noise:{type:"white"},envelope:{attack:.005,decay:.1,sustain:0}},t.NoiseSynth.prototype.triggerAttack=function(t,e){return this.envelope.triggerAttack(t,e),this},t.NoiseSynth.prototype.triggerRelease=function(t){return this.envelope.triggerRelease(t),this},t.NoiseSynth.prototype.triggerAttackRelease=function(t,e,i){return e=this.toSeconds(e),t=this.toSeconds(t),this.triggerAttack(e,i),this.triggerRelease(e+t),this},t.NoiseSynth.prototype.dispose=function(){return t.Instrument.prototype.dispose.call(this),this._writable(["noise","envelope"]),this.noise.dispose(),this.noise=null,this.envelope.dispose(),this.envelope=null,this},t.NoiseSynth}),e(function(t){return t.PluckSynth=function(e){e=this.defaultArg(e,t.PluckSynth.defaults),t.Instrument.call(this,e),this._noise=new t.Noise("pink"),this.attackNoise=1,this._lfcf=new t.LowpassCombFilter({resonance:e.resonance,dampening:e.dampening}),this.resonance=this._lfcf.resonance,this.dampening=this._lfcf.dampening,this._noise.connect(this._lfcf),this._lfcf.connect(this.output),this._readOnly(["resonance","dampening"])},t.extend(t.PluckSynth,t.Instrument),t.PluckSynth.defaults={attackNoise:1,dampening:4e3,resonance:.9},t.PluckSynth.prototype.triggerAttack=function(t,e){t=this.toFrequency(t),e=this.toSeconds(e);var i=1/t;return this._lfcf.delayTime.setValueAtTime(i,e),this._noise.start(e),this._noise.stop(e+i*this.attackNoise),this},t.PluckSynth.prototype.dispose=function(){return t.Instrument.prototype.dispose.call(this),this._noise.dispose(),this._lfcf.dispose(),this._noise=null,this._lfcf=null,this._writable(["resonance","dampening"]),this.dampening=null,this.resonance=null,this},t.PluckSynth}),e(function(t){return t.PolySynth=function(){var e,i,s;for(t.Instrument.call(this),e=this.optionsObject(arguments,["polyphony","voice"],t.PolySynth.defaults),e=this.defaultArg(e,t.Instrument.defaults),e.polyphony=Math.min(t.PolySynth.MAX_POLYPHONY,e.polyphony),this.voices=new Array(e.polyphony),this._triggers=new Array(e.polyphony),this.detune=new t.Signal(e.detune,t.Type.Cents),this._readOnly("detune"),i=0;ie&&(o.voice.triggerRelease(e),o.release=e);return this},t.PolySynth.prototype.set=function(t,e,i){for(var s=0;st&&(i.release=t,i.voice.triggerRelease(t));return this},t.PolySynth.prototype.dispose=function(){t.Instrument.prototype.dispose.call(this);for(var e=0;ethis._startTime?t.State.Started:t.State.Stopped}}),t.BufferSource.prototype.start=function(t,e,i,s,n){if(-1!==this._startTime)throw new Error("Tone.BufferSource: can only be started once.");if(!this.buffer)throw new Error("Tone.BufferSource: no buffer set.");return t=this.toSeconds(t),e=this.loop?this.defaultArg(e,this.loopStart):this.defaultArg(e,0),e=this.toSeconds(e),t=this.toSeconds(t),this._source.start(t,e),s=this.defaultArg(s,1),this._gain=s,n=this.isUndef(n)?this.toSeconds(this.fadeIn):this.toSeconds(n),n>0?(this._gainNode.gain.setValueAtTime(0,t),this._gainNode.gain.linearRampToValueAtTime(this._gain,t+n)):this._gainNode.gain.setValueAtTime(s,t),this._startTime=t+n,this.isUndef(i)||(i=this.defaultArg(i,this.buffer.duration-e),i=this.toSeconds(i),this.stop(t+i+n,n)),this},t.BufferSource.prototype.stop=function(t,e){if(!this.buffer)throw new Error("Tone.BufferSource: no buffer set.");return t=this.toSeconds(t),e=this.isUndef(e)?this.toSeconds(this.fadeOut):this.toSeconds(e),this._gainNode.gain.cancelScheduledValues(this._startTime+this.sampleTime),e>0?(this._gainNode.gain.setValueAtTime(this._gain,t),this._gainNode.gain.linearRampToValueAtTime(0,t+e),t+=e):this._gainNode.gain.setValueAtTime(0,t),this._source.stop(t),this},t.BufferSource.prototype._onended=function(){this.onended(this),this.dispose()},Object.defineProperty(t.BufferSource.prototype,"loopStart",{get:function(){return this._source.loopStart},set:function(t){this._source.loopStart=this.toSeconds(t)}}),Object.defineProperty(t.BufferSource.prototype,"loopEnd",{get:function(){return this._source.loopEnd},set:function(t){this._source.loopEnd=this.toSeconds(t)}}),Object.defineProperty(t.BufferSource.prototype,"buffer",{get:function(){return this._source.buffer},set:function(e){e instanceof t.Buffer?this._source.buffer=e.get():this._source.buffer=e}}),Object.defineProperty(t.BufferSource.prototype,"loop",{get:function(){return this._source.loop},set:function(t){this._source.loop=t}}),t.BufferSource.prototype.dispose=function(){return this.onended=null,this._source&&(this._source.onended=null,this._source.disconnect(),this._source=null),this._gainNode&&(this._gainNode.disconnect(),this._gainNode=null),this._startTime=-1,this.playbackRate=null,this.output=null,this},t.BufferSource}),e(function(t){return navigator.getUserMedia=navigator.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.msGetUserMedia,t.ExternalInput=function(){var e=this.optionsObject(arguments,["inputNum"],t.ExternalInput.defaults);t.Source.call(this,e),this._mediaStream=null,this._stream=null,this._constraints={audio:!0},this._inputNum=e.inputNum,this._gate=new t.Gain(0).connect(this.output)},t.extend(t.ExternalInput,t.Source),t.ExternalInput.defaults={inputNum:0},t.ExternalInput.prototype._getUserMedia=function(e,i){t.ExternalInput.supported||i("browser does not support 'getUserMedia'"),t.ExternalInput.sources[this._inputNum]&&(this._constraints={audio:{optional:[{sourceId:t.ExternalInput.sources[this._inputNum].id}]}}),navigator.getUserMedia(this._constraints,function(t){this._onStream(t),e()}.bind(this),function(t){i(t)})},t.ExternalInput.prototype._onStream=function(t){if(!this.isFunction(this.context.createMediaStreamSource))throw new Error("Tone.ExternalInput: browser does not support the 'MediaStreamSourceNode'");this._stream||(this._stream=t,this._mediaStream=this.context.createMediaStreamSource(t),this._mediaStream.connect(this._gate))},t.ExternalInput.prototype.open=function(e,i){return e=this.defaultArg(e,t.noOp),i=this.defaultArg(i,t.noOp),t.ExternalInput.getSources(function(){this._getUserMedia(e,i)}.bind(this)),this},t.ExternalInput.prototype.close=function(){if(this._stream){var t=this._stream.getTracks()[this._inputNum];this.isUndef(t)||t.stop(),this._stream=null}return this},t.ExternalInput.prototype._start=function(t){return t=this.toSeconds(t),this._gate.gain.setValueAtTime(1,t),this},t.ExternalInput.prototype._stop=function(t){return t=this.toSeconds(t),this._gate.gain.setValueAtTime(0,t),this},t.ExternalInput.prototype.dispose=function(){return t.Source.prototype.dispose.call(this),this.close(),this._mediaStream&&(this._mediaStream.disconnect(),this._mediaStream=null),this._constraints=null,this._gate.dispose(),this._gate=null,this},t.ExternalInput.sources=[],t.ExternalInput._canGetSources=!t.prototype.isUndef(window.MediaStreamTrack)&&t.prototype.isFunction(MediaStreamTrack.getSources),Object.defineProperty(t.ExternalInput,"supported",{get:function(){return t.prototype.isFunction(navigator.getUserMedia)}}),t.ExternalInput.getSources=function(e){return 0===t.ExternalInput.sources.length&&t.ExternalInput._canGetSources?MediaStreamTrack.getSources(function(i){for(var s=0;s0?t.State.Started:t.State.Stopped}}),Object.defineProperty(t.MultiPlayer.prototype,"mute",{get:function(){return this._volume.mute},set:function(t){this._volume.mute=t}}),t.MultiPlayer.prototype.dispose=function(){t.prototype.dispose.call(this),this._volume.dispose(),this._volume=null,this._writable("volume"),this.volume=null,this.buffers.dispose(),this.buffers=null;for(var e=0;e0&&(a=this._loopEnd),e=(2*Math.random()-1)*this.drift,i=this._offset-this._overlap+e,s=this.detune/100,n=this._player.fadeIn,this.loop&&this._offset>a?(o=this._offset-a,this._player.start(this.buffer,t,i,o+this._overlap,s),i=this._offset%a,this._offset=this._loopStart,this._player.fadeIn=0,this._player.start(this.buffer,t+o,this._offset,i+this._overlap,s)):this._offset>a?this.stop(t):(0>i&&(this._player.fadeIn=Math.max(this._player.fadeIn+i,0),i=0),this._player.start(this.buffer,t,i,this.grainSize+this._overlap,s)),this._player.fadeIn=n,r=this._clock._nextTick-t,this._offset+=r*this._playbackRate},t.GrainPlayer.prototype.scrub=function(t,e){return this._offset=this.toSeconds(t),this._tick(this.toSeconds(e)),this},Object.defineProperty(t.GrainPlayer.prototype,"playbackRate",{get:function(){return this._playbackRate},set:function(t){this._playbackRate=t,this.grainSize=this._grainSize}}),Object.defineProperty(t.GrainPlayer.prototype,"loopStart",{get:function(){return this._loopStart},set:function(t){this._loopStart=this.toSeconds(t)}}),Object.defineProperty(t.GrainPlayer.prototype,"loopEnd",{get:function(){return this._loopEnd},set:function(t){this._loopEnd=this.toSeconds(t)}}),Object.defineProperty(t.GrainPlayer.prototype,"reverse",{get:function(){return this.buffer.reverse},set:function(t){this.buffer.reverse=t}}),Object.defineProperty(t.GrainPlayer.prototype,"grainSize",{get:function(){return this._grainSize},set:function(t){this._grainSize=this.toSeconds(t),this._clock.frequency.value=this._playbackRate/this._grainSize}}),Object.defineProperty(t.GrainPlayer.prototype,"overlap",{get:function(){return this._overlap},set:function(t){t=this.toSeconds(t),this._overlap=t,this._overlap<0?(this._player.fadeIn=.01,this._player.fadeOut=.01):(this._player.fadeIn=t,this._player.fadeOut=t)}}),t.GrainPlayer.prototype.dispose=function(){return t.Source.prototype.dispose.call(this),this.buffer.dispose(),this.buffer=null,this._player.dispose(),this._player=null,this._clock.dispose(),this._clock=null,this},t.GrainPlayer}),e(function(t){return t.Microphone=function(){t.ExternalInput.call(this,0)},t.extend(t.Microphone,t.ExternalInput),Object.defineProperty(t.Microphone,"supported",{get:function(){return t.ExternalInput.supported}}),t.Microphone}),i}); \ No newline at end of file diff --git a/audioLocal.js b/audioLocal.js new file mode 100644 index 00000000..1f31a1a1 --- /dev/null +++ b/audioLocal.js @@ -0,0 +1,94 @@ +function AudioLocal () { + + this.tone = new Tone(); + + // effects setup + + this.delay = new Tone.FeedbackDelay(0.25, 0.5); + this.delay.wet.value = 0; + + this.pitchShift = new Tone.PitchShift(); + + this.panner = new Tone.Panner(); + + this.reverb = new Tone.Freeverb(); + this.reverb.wet.value = 0; + + Tone.Master.chain(this.delay, this.pitchShift, this.panner, this.reverb); + + // synth setup for play note block + + this.synth = new Tone.PolySynth(6, Tone.Synth).toMaster(); + + // drum sounds + + var drumFileNames = ['high_conga', 'small_cowbell', 'snare_drum', 'splash cymbal']; + this.drumSamplers = this._loadSoundFiles(drumFileNames); + + // sound files + + var soundFileNames = ['meow','boing','this_is_a_test','who_put_the_bomp','cave','drip_drop','drum_machine','eggs','zoop']; + this.soundSamplers = this._loadSoundFiles(soundFileNames); +} + +AudioLocal.prototype.helloWorld = function () { + console.log('it lives'); +}; + +AudioLocal.prototype._loadSoundFiles = function(filenames) { + var samplers = []; + + for (var name of filenames) { + + // create an array of samplers for each sound (a hack to get polyphony for each sound) + var myVoices = []; + for (var i=0; i<6; i++) { + var p = new Tone.Sampler('sounds/' + name + '.mp3').toMaster(); + myVoices.push(p); + } + + var polySampler = { + voices : myVoices, + currentVoice : 0, + nextVoice : function() {return this.voices[this.currentVoice++ % this.voices.length];}, + stopAllVoices : function() {for (var i=0;i + + + diff --git a/playground/playground.js b/playground/playground.js index 3592df1d..49cc74aa 100644 --- a/playground/playground.js +++ b/playground/playground.js @@ -28,6 +28,10 @@ window.onload = function() { window.renderer = new window.RenderWebGLLocal(canvas); window.renderer.connectWorker(window.vm.vmWorker); + // Instantiate audio engine and connect it to the VM + window.audioLocal = new window.AudioLocal(); + window.audioLocal.connectWorker(window.vm.vmWorker); + // Instantiate scratch-blocks and attach it to the DOM. var toolbox = document.getElementById('toolbox'); var workspace = window.Blockly.inject('blocks', { diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index ecd32676..4706c7d6 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -33,7 +33,8 @@ Scratch3SoundBlocks.prototype.getPrimitives = function() { }; Scratch3SoundBlocks.prototype.playSound = function (args, util) { - self.postMessage({method: 'playsound', soundnum:args.SOUND_NUM}); + // self.postMessage({method: 'playsound', soundnum:args.SOUND_NUM}); + util.target.playSound(args.SOUND_NUM); }; Scratch3SoundBlocks.prototype.playSoundWithPitch = function (args, util) { diff --git a/src/index.js b/src/index.js index e5c19ee1..34f10eaa 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,8 @@ var util = require('util'); var Runtime = require('./engine/runtime'); var sb2import = require('./import/sb2import'); +var AudioWorker = require('../audioWorker'); + /** * Whether the environment is a WebWorker. * @const{boolean} @@ -174,6 +176,7 @@ if (ENV_WORKER) { './node_modules/scratch-render/render-worker.js' ); self.renderer = new self.RenderWebGLWorker(); + self.audioWorker = new AudioWorker(); self.vmInstance = new VirtualMachine(); self.onmessage = function (e) { var messageData = e.data; diff --git a/src/sprites/clone.js b/src/sprites/clone.js index 408667ab..4f2c20a1 100644 --- a/src/sprites/clone.js +++ b/src/sprites/clone.js @@ -31,6 +31,9 @@ function Clone(sprite) { this.drawableID = null; this.initDrawable(); + + // audio engine + this.audioWorker = self.audioWorker; } util.inherits(Clone, Target); @@ -235,4 +238,10 @@ Clone.prototype.getName = function () { return this.sprite.name; }; +// Audio + +Clone.prototype.playSound = function (soundNum) { + this.audioWorker.playSound(soundNum); +} + module.exports = Clone; diff --git a/src/worker.js b/src/worker.js index a8069ded..9f2b93ec 100644 --- a/src/worker.js +++ b/src/worker.js @@ -1,6 +1,6 @@ var EventEmitter = require('events'); var util = require('util'); -var Tone = require('tone'); +// var Tone = require('tone'); function VirtualMachine () { if (!window.Worker) { @@ -12,6 +12,7 @@ function VirtualMachine () { EventEmitter.call(instance); instance.vmWorker = new Worker('../vm.js'); +/* // MUSIC STUFF by ericr // tone setup @@ -115,15 +116,16 @@ function VirtualMachine () { }; function playNoteForBeats(note, beats) { -// var freq = midiToFreq(note); - var midiNote = scaleNoteToMidiNote(note, currentScale, rootNote); var freq = midiToFreq(midiNote); synth.triggerAttackRelease(freq, beats, quantizeUnit); } + */ + // onmessage calls are converted into emitted events. instance.vmWorker.onmessage = function (e) { + /* switch (e.data.method) { case 'playsound': soundSamplers[e.data.soundnum].nextVoice().triggerAttack(0, quantizeUnit); @@ -195,6 +197,7 @@ function VirtualMachine () { } break; } + */ instance.emit(e.data.method, e.data); }; From d9a703b4add5d088ab33681b8928ee9a494d938c Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 13 Sep 2016 17:52:54 -0400 Subject: [PATCH 011/721] play sound and play note block --- audioLocal.js | 42 ++++++++++----------- audioWorker.js | 16 ++++++++ playground/index.html | 59 +---------------------------- src/blocks/scratch3_sound.js | 72 ++++-------------------------------- src/sprites/clone.js | 8 ++++ 5 files changed, 54 insertions(+), 143 deletions(-) diff --git a/audioLocal.js b/audioLocal.js index 1f31a1a1..4c1eb773 100644 --- a/audioLocal.js +++ b/audioLocal.js @@ -39,29 +39,15 @@ AudioLocal.prototype._loadSoundFiles = function(filenames) { var samplers = []; for (var name of filenames) { - - // create an array of samplers for each sound (a hack to get polyphony for each sound) - var myVoices = []; - for (var i=0; i<6; i++) { - var p = new Tone.Sampler('sounds/' + name + '.mp3').toMaster(); - myVoices.push(p); - } - - var polySampler = { - voices : myVoices, - currentVoice : 0, - nextVoice : function() {return this.voices[this.currentVoice++ % this.voices.length];}, - stopAllVoices : function() {for (var i=0;i - - - - - - - - 1 - - - - - - - - 1 - - - - @@ -320,18 +300,10 @@ - - - - 1 - - - - - 1 + 60 @@ -339,34 +311,7 @@ - - - - - 1 - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 4706c7d6..e0777ab4 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -13,74 +13,34 @@ function Scratch3SoundBlocks(runtime) { Scratch3SoundBlocks.prototype.getPrimitives = function() { return { 'sound_playsound': this.playSound, - 'sound_playwithpitch': this.playSoundWithPitch, + // 'sound_playsoundandwait': this.playSoundAndWait, 'sound_stopallsounds': this.stopAllSounds, - 'sound_playnote': this.playNote, 'sound_playnoteforbeats': this.playNoteForBeats, - 'sound_scalenotetomidinote': this.scaleNoteToMidiNote, - 'sound_playdrum': this.playDrum, 'sound_playdrumforbeats': this.playDrumForBeats, - 'sound_setkey' : this.setKey, 'sound_seteffectto' : this.setEffect, 'sound_changeeffectby' : this.changeEffect, 'sound_cleareffects' : this.clearEffects, - 'sound_scales_menu' : this.scalesMenu, 'sound_sounds_menu' : this.soundsMenu, - 'sound_roots_menu' : this.rootsMenu, 'sound_beats_menu' : this.beatsMenu, 'sound_effects_menu' : this.effectsMenu, }; }; Scratch3SoundBlocks.prototype.playSound = function (args, util) { - // self.postMessage({method: 'playsound', soundnum:args.SOUND_NUM}); util.target.playSound(args.SOUND_NUM); }; -Scratch3SoundBlocks.prototype.playSoundWithPitch = function (args, util) { - self.postMessage({method: 'playsoundwithpitch', soundnum:args.SOUND_NUM, pitch:args.PITCH}); -}; - Scratch3SoundBlocks.prototype.stopAllSounds = function (args, util) { - self.postMessage({method: 'stopallsounds'}); + util.target.stopAllSounds(); }; Scratch3SoundBlocks.prototype.playNoteForBeats = function (args, util) { - self.postMessage({method: 'playnoteforbeats', note:args.NOTE, beats:args.BEATS}); - return new Promise(function(resolve) { - setTimeout(function() { - resolve(); - }, (1000 * args.BEATS) ); - }); -}; - -Scratch3SoundBlocks.prototype.playNote = function (args, util) { - self.postMessage({method: 'playnote', note:args.NOTE}); -}; - -Scratch3SoundBlocks.prototype.scaleNoteToMidiNote = function (args, util) { - - var root = parseInt(args.ROOT) + 60; - - var scales = { - 'MAJOR' : [0,2,4,5,7,9,11], - 'MINOR' : [0,2,3,5,7,8,10], - 'PENTATONIC': [0, 2, 4, 7, 9], - 'CHROMATIC' : [0,1,2,3,4,5,6,7,8,9,10,11], - }; - - var scale = scales[args.SCALE]; - - var scaleNote = args.NOTE; - - var scaleIndex = (Math.round(scaleNote) - 1) % scale.length; - if (scaleIndex < 0) { - scaleIndex += scale.length; - } - var octave = Math.floor((scaleNote - 1) / scale.length); - var midiNote = root + (octave * 12) + scale[scaleIndex]; - - return midiNote; + util.target.playNoteForBeats(args.NOTE, args.BEATS); + return new Promise(function(resolve) { + setTimeout(function() { + resolve(); + }, (1000 * args.BEATS) ); + }); }; Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) { @@ -92,14 +52,6 @@ Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) { }); }; -Scratch3SoundBlocks.prototype.playDrum = function (args, util) { - self.postMessage({method: 'playdrum', drum:args.DRUMTYPE}); -}; - -Scratch3SoundBlocks.prototype.setKey = function (args, util) { - self.postMessage({method: 'setkey', root:args.ROOT, scale:args.SCALE}); -}; - Scratch3SoundBlocks.prototype.setEffect = function (args, util) { self.postMessage({method: 'seteffect', effect:args.EFFECT, value:args.VALUE}); }; @@ -116,14 +68,6 @@ Scratch3SoundBlocks.prototype.soundsMenu = function (args, util) { return args.SOUND_MENU; }; -Scratch3SoundBlocks.prototype.scalesMenu = function (args, util) { - return args.SCALE; -}; - -Scratch3SoundBlocks.prototype.rootsMenu = function (args, util) { - return args.ROOT; -}; - Scratch3SoundBlocks.prototype.beatsMenu = function (args, util) { return args.BEATS; }; diff --git a/src/sprites/clone.js b/src/sprites/clone.js index 4f2c20a1..523a00fd 100644 --- a/src/sprites/clone.js +++ b/src/sprites/clone.js @@ -244,4 +244,12 @@ Clone.prototype.playSound = function (soundNum) { this.audioWorker.playSound(soundNum); } +Clone.prototype.stopAllSounds = function () { + this.audioWorker.stopAllSounds(); +} + +Clone.prototype.playNoteForBeats = function (note,beats) { + this.audioWorker.playNoteForBeats(note, beats); +} + module.exports = Clone; From e19c1942c3fa81c9babf61960993817e7fa29fec Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 13 Sep 2016 18:09:43 -0400 Subject: [PATCH 012/721] Merge remote-tracking branch 'LLK/develop' into develop # Conflicts: # src/index.js # src/sprites/clone.js # src/worker.js --- assets/scratch_cat.svg | 42 + assets/stage.png | Bin 0 -> 1346 bytes playground/index.html | 33 +- playground/playground.css | 36 +- playground/playground.js | 44 +- src/blocks/scratch3_control.js | 16 +- src/blocks/scratch3_event.js | 17 +- src/blocks/scratch3_looks.js | 131 +- src/blocks/scratch3_motion.js | 68 +- src/blocks/scratch3_operators.js | 30 +- src/blocks/scratch3_sensing.js | 33 +- src/engine/adapter.js | 12 +- src/engine/blocks.js | 35 +- src/engine/execute.js | 118 +- src/engine/runtime.js | 85 +- src/engine/thread.js | 6 + src/import/sb2import.js | 20 +- src/index.js | 182 +- src/io/keyboard.js | 25 +- src/io/mouse.js | 33 +- src/sprites/clone.js | 70 +- src/util/cast.js | 17 + src/util/color.js | 71 +- src/util/timer.js | 64 +- src/util/xml-escape.js | 21 + src/worker.js | 276 - test/unit/blocks.js | 61 + vm.js | 9658 ++++++++++++++++++++---------- vm.min.js | 12 +- vm.worker.js | 1169 ---- webpack.config.js | 3 +- 31 files changed, 7363 insertions(+), 5025 deletions(-) create mode 100644 assets/scratch_cat.svg create mode 100644 assets/stage.png create mode 100644 src/util/xml-escape.js delete mode 100644 src/worker.js delete mode 100644 vm.worker.js diff --git a/assets/scratch_cat.svg b/assets/scratch_cat.svg new file mode 100644 index 00000000..82345546 --- /dev/null +++ b/assets/scratch_cat.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/stage.png b/assets/stage.png new file mode 100644 index 0000000000000000000000000000000000000000..c60861c2bd315d6eaffdb9dfb632abb49e1102d4 GIT binary patch literal 1346 zcmeAS@N?(olHy`uVBq!ia0y~yV0-|?85~SN5r)wHmdKI;Vst00K&1Q~&?~ literal 0 HcmV?d00001 diff --git a/playground/index.html b/playground/index.html index 73182ec3..dacd4c4f 100644 --- a/playground/index.html +++ b/playground/index.html @@ -10,18 +10,21 @@

Scratch VM Playground

- - -

- Renderer
- VM Threads
- VM Block Representation
- Import/Export -

+
+ + +
+
+
- Render
- + Renderer
+
Thread explorer @@ -32,14 +35,14 @@

         
- Import/Export - -
+ Import/Export
+ Project ID: +

  -

+

@@ -751,7 +754,7 @@ - + - - + + diff --git a/playground/playground.js b/playground/playground.js index fa91cd27..e5c61ec8 100644 --- a/playground/playground.js +++ b/playground/playground.js @@ -36,9 +36,8 @@ window.onload = function() { var canvas = document.getElementById('scratch-stage'); window.renderer = new window.RenderWebGLLocal(canvas); - // Instantiate audio engine and connect it to the VM - window.audioLocal = new window.AudioLocal(); - window.audioLocal.connectWorker(window.vm.vmWorker); + // Instantiate audio engine + window.audioEngine = new window.AudioEngine(); // Instantiate scratch-blocks and attach it to the DOM. var toolbox = document.getElementById('toolbox'); diff --git a/src/sprites/clone.js b/src/sprites/clone.js index 8c7f79b9..01407ca2 100644 --- a/src/sprites/clone.js +++ b/src/sprites/clone.js @@ -280,7 +280,7 @@ Clone.prototype.getName = function () { // Audio Clone.prototype.playSound = function (soundNum) { - this.audioWorker.playSound(soundNum); + window.audioEngine.playSound(soundNum); } Clone.prototype.stopAllSounds = function () { From 47c0f18e85a782043244137a24861fb2acd3d3e6 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 15 Sep 2016 16:51:24 -0400 Subject: [PATCH 014/721] adding more sound blocks --- AudioEngine.js | 37 +++++++++++++++++++++--------------- src/blocks/scratch3_sound.js | 8 ++++---- src/sprites/clone.js | 8 ++++++-- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/AudioEngine.js b/AudioEngine.js index 8667e146..ee02a013 100644 --- a/AudioEngine.js +++ b/AudioEngine.js @@ -32,11 +32,29 @@ function AudioEngine () { } AudioEngine.prototype.playSound = function (soundNum) { - this.soundSamplers[soundNum].triggerAttack(); + this.soundSamplers[soundNum].triggerAttack(); }; -AudioEngine.prototype.helloWorld = function () { - console.log('it lives'); +AudioEngine.prototype.playNoteForBeats = function(note, beats) { + var freq = this._midiToFreq(note); + this.synth.triggerAttackRelease(freq, beats); +}; + +AudioEngine.prototype.playDrumForBeats = function(drumNum, beats) { + this.drumSamplers[drumNum].triggerAttack(); +}; + +AudioEngine.prototype.stopAllSounds = function() { + // stop synth notes + this.synth.releaseAll(); + // stop drum notes + for (var i=0; i Date: Tue, 27 Sep 2016 17:09:53 -0400 Subject: [PATCH 015/721] adding sound blocks --- AudioEngine.js | 50 +++++++++++++++++++++++++++++++++++- src/blocks/scratch3_sound.js | 23 +++++++++++++---- src/index.js | 4 --- src/sprites/clone.js | 20 --------------- 4 files changed, 67 insertions(+), 30 deletions(-) diff --git a/AudioEngine.js b/AudioEngine.js index ee02a013..8b5aa036 100644 --- a/AudioEngine.js +++ b/AudioEngine.js @@ -27,7 +27,7 @@ function AudioEngine () { // sound files - var soundFileNames = ['meow','boing','this_is_a_test','who_put_the_bomp','cave','drip_drop','drum_machine','eggs','zoop']; + var soundFileNames = ['meow','boing','cave','drip_drop','drum_machine','eggs','zoop']; this.soundSamplers = this._loadSoundFiles(soundFileNames); } @@ -35,6 +35,10 @@ AudioEngine.prototype.playSound = function (soundNum) { this.soundSamplers[soundNum].triggerAttack(); }; +AudioEngine.prototype.getSoundDuration = function (soundNum) { + return this.soundSamplers[soundNum].player.buffer.duration; +}; + AudioEngine.prototype.playNoteForBeats = function(note, beats) { var freq = this._midiToFreq(note); this.synth.triggerAttackRelease(freq, beats); @@ -57,6 +61,50 @@ AudioEngine.prototype.stopAllSounds = function() { } }; +AudioEngine.prototype.setEffect = function(effect, value) { + switch (effect) { + case 'ECHO': + this.delay.wet.value = (value / 100) / 2; // max 50% wet (need dry signal too) + break; + case 'PAN': + this.panner.pan.value = value / 100; + break; + case 'REVERB': + this.reverb.wet.value = value / 100; + break; + case 'PITCH': + this.pitchShift.pitch = value / 20; // arbitrary scaling of 20 per semitone, for now... default 100 is a perfect fourth + break; + } +} + +AudioEngine.prototype.changeEffect = function(effect, value) { + switch (effect) { + case 'ECHO': + this.delay.wet.value += (value / 100) / 2; // max 50% wet (need dry signal too) + this.delay.wet.value = this._clamp(this.delay.wet.value, 0, 0.5); + break; + case 'PAN': + this.panner.pan.value += value / 100; + this.panner.pan.value = this._clamp(this.panner.pan.value, -1, 1); + break; + case 'REVERB': + this.reverb.wet.value += value / 100; + this.reverb.wet.value = this._clamp(this.reverb.wet.value, 0, 1); + break; + case 'PITCH': + this.pitchShift.pitch += value / 20; + break; + } +} + +AudioEngine.prototype.clearEffects = function() { + this.delay.wet.value = 0; + this.panner.pan.value = 0; + this.reverb.wet.value = 0; + this.pitchShift.pitch = 0; +} + AudioEngine.prototype._loadSoundFiles = function(filenames) { var samplers = []; diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index a6f4c04c..5eb5b33f 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -16,7 +16,7 @@ function Scratch3SoundBlocks(runtime) { Scratch3SoundBlocks.prototype.getPrimitives = function() { return { 'sound_playsound': this.playSound, - // 'sound_playsoundandwait': this.playSoundAndWait, + 'sound_playuntildone': this.playSoundAndWait, 'sound_stopallsounds': this.stopAllSounds, 'sound_playnoteforbeats': this.playNoteForBeats, 'sound_playdrumforbeats': this.playDrumForBeats, @@ -30,15 +30,25 @@ Scratch3SoundBlocks.prototype.getPrimitives = function() { }; Scratch3SoundBlocks.prototype.playSound = function (args, util) { - util.target.playSound(args.SOUND_NUM); + window.audioEngine.playSound(args.SOUND_NUM); +}; + +Scratch3SoundBlocks.prototype.playSoundAndWait = function (args, util) { + window.audioEngine.playSound(args.SOUND_NUM); + var duration = window.audioEngine.getSoundDuration(args.SOUND_NUM); + return new Promise(function(resolve) { + setTimeout(function() { + resolve(); + }, 1000*duration); + }); }; Scratch3SoundBlocks.prototype.stopAllSounds = function (args, util) { - util.target.stopAllSounds(); + window.audioEngine.stopAllSounds(); }; Scratch3SoundBlocks.prototype.playNoteForBeats = function (args, util) { - util.target.playNoteForBeats(args.NOTE, args.BEATS); + window.audioEngine.playNoteForBeats(args.NOTE, args.BEATS); return new Promise(function(resolve) { setTimeout(function() { resolve(); @@ -47,7 +57,7 @@ Scratch3SoundBlocks.prototype.playNoteForBeats = function (args, util) { }; Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) { - util.target.playDrumForBeats(args.DRUMTYPE, args.BEATS); + window.audioEngine.playDrumForBeats(args.DRUMTYPE, args.BEATS); return new Promise(function(resolve) { setTimeout(function() { resolve(); @@ -56,12 +66,15 @@ Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) { }; Scratch3SoundBlocks.prototype.setEffect = function (args, util) { + window.audioEngine.setEffect(args.EFFECT, args.VALUE); }; Scratch3SoundBlocks.prototype.changeEffect = function (args, util) { + window.audioEngine.changeEffect(args.EFFECT, args.VALUE); }; Scratch3SoundBlocks.prototype.clearEffects = function (args, util) { + window.audioEngine.clearEffects(); }; Scratch3SoundBlocks.prototype.soundsMenu = function (args, util) { diff --git a/src/index.js b/src/index.js index aafed8e2..a51bae13 100644 --- a/src/index.js +++ b/src/index.js @@ -224,10 +224,6 @@ VirtualMachine.prototype.emitWorkspaceUpdate = function () { 'xml': this.editingTarget.blocks.toXML() }); }; -<<<<<<< HEAD - -======= ->>>>>>> LLK/develop /** * Export and bind to `window` */ diff --git a/src/sprites/clone.js b/src/sprites/clone.js index 185a0e4f..d6a2072e 100644 --- a/src/sprites/clone.js +++ b/src/sprites/clone.js @@ -32,8 +32,6 @@ function Clone(sprite) { this.initDrawable(); - // audio engine - this.audioWorker = self.audioWorker; } util.inherits(Clone, Target); @@ -277,24 +275,6 @@ Clone.prototype.getName = function () { return this.sprite.name; }; -// Audio - -Clone.prototype.playSound = function (soundNum) { - window.audioEngine.playSound(soundNum); -} - -Clone.prototype.stopAllSounds = function () { - window.audioEngine.stopAllSounds(); -} - -Clone.prototype.playNoteForBeats = function (note,beats) { - window.audioEngine.playNoteForBeats(note, beats); -} - -Clone.prototype.playDrumForBeats = function (drumNum,beats) { - window.audioEngine.playDrumForBeats(drumNum, beats); -} - /** * Return whether the clone is touching a color. * @param {Array.} rgb [r,g,b], values between 0-255. From 54c5c82fde0ab514097db25de9201a6897b4750e Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 27 Sep 2016 18:18:15 -0400 Subject: [PATCH 016/721] moved audio engine files --- AudioEngine.js => playground/AudioEngine.js | 0 Tone.min.js => playground/Tone.min.js | 0 playground/index.html | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename AudioEngine.js => playground/AudioEngine.js (100%) rename Tone.min.js => playground/Tone.min.js (100%) diff --git a/AudioEngine.js b/playground/AudioEngine.js similarity index 100% rename from AudioEngine.js rename to playground/AudioEngine.js diff --git a/Tone.min.js b/playground/Tone.min.js similarity index 100% rename from Tone.min.js rename to playground/Tone.min.js diff --git a/playground/index.html b/playground/index.html index f9d029a2..6287cfa8 100644 --- a/playground/index.html +++ b/playground/index.html @@ -803,8 +803,8 @@ - - + + + From 7fe597c9f98058579b8a16f8769e2a4dafc88927 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sat, 3 Dec 2016 16:29:56 -0500 Subject: [PATCH 113/721] Now Actually loads Rendered Target Properties --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index d6fb1107..d25ce3c7 100644 --- a/src/index.js +++ b/src/index.js @@ -189,7 +189,7 @@ VirtualMachine.prototype.fromJSON = function (json) { if (x === 'sprite') { continue; } - target[x] = obj.sprites[x]; + target[x] = obj.sprites[i][x]; } } this.editingTarget = this.runtime.targets[1]; From c4750d64b6e15a32fccc004cc54995837f2f3be2 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sat, 3 Dec 2016 16:50:06 -0500 Subject: [PATCH 114/721] Fix Drawable Properties Not Getting Updated --- src/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.js b/src/index.js index d25ce3c7..1d64030d 100644 --- a/src/index.js +++ b/src/index.js @@ -191,6 +191,7 @@ VirtualMachine.prototype.fromJSON = function (json) { } target[x] = obj.sprites[i][x]; } + target.updateAllDrawablePropertie(); } this.editingTarget = this.runtime.targets[1]; // Update the VM user's knowledge of targets and blocks on the workspace. From a58bcb93e64cf0b941574b95aeea67d06e81ae6d Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sun, 4 Dec 2016 08:39:22 -0500 Subject: [PATCH 115/721] Update index.js --- src/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 1d64030d..73f2e788 100644 --- a/src/index.js +++ b/src/index.js @@ -155,13 +155,17 @@ VirtualMachine.prototype.loadProject = function (json) { this.runtime.setEditingTarget(this.editingTarget); }; +VirtualMachine.prototype.toPrettyJSON = function () { + return JSON.stringify(this.toJSON(), null, 4); +} + VirtualMachine.prototype.toJSON = function () { var obj = new Object(); obj.sprites = this.runtime.targets; obj.meta = new Object(); obj.meta.name = 'WIP'; obj.meta.useragent = navigator.userAgent; - return JSON.stringify(obj, null, 2); + return obj; }; VirtualMachine.prototype.fromJSON = function (json) { From 532df2079b72ca4ed01157169ae2404fb1796c94 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sun, 4 Dec 2016 08:39:46 -0500 Subject: [PATCH 116/721] Update index.html --- playground/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playground/index.html b/playground/index.html index b981ef2f..13c345b2 100644 --- a/playground/index.html +++ b/playground/index.html @@ -72,7 +72,7 @@ - - - - - + From 8e84b2757e579602f96a44627d6c3c2a9ad79d3a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 6 Apr 2017 09:37:39 -0400 Subject: [PATCH 285/721] Report costume and sound data in sprite json --- src/sprites/rendered-target.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 98172f67..a032b1ec 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -440,6 +440,14 @@ RenderedTarget.prototype.getCostumes = function () { return this.sprite.costumes; }; +/** + * Get full sound list + * @return {object[]} list of sounds + */ +RenderedTarget.prototype.getSounds = function () { + return this.sprite.sounds; +}; + /** * Update all drawable properties for this rendered target. * Use when a batch has changed, e.g., when the drawable is first created. @@ -767,6 +775,8 @@ RenderedTarget.prototype.toJSON = function () { direction: this.direction, draggable: this.draggable, costume: this.getCurrentCostume(), + costumes: this.getCostumes(), + sounds: this.getSounds(), costumeCount: this.getCostumes().length, visible: this.visible, rotationStyle: this.rotationStyle From 80e8003fb23c1d1aee22c53d65c628d53cc496e1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 6 Apr 2017 09:46:40 -0400 Subject: [PATCH 286/721] Add tests for rendered target --- test/unit/sprites_rendered-target.js | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/unit/sprites_rendered-target.js b/test/unit/sprites_rendered-target.js index 3ff40e2c..4a8b9fb9 100644 --- a/test/unit/sprites_rendered-target.js +++ b/test/unit/sprites_rendered-target.js @@ -21,3 +21,33 @@ test('#stopAll clears graphics effects', function (t) { t.equals(a.effects[effectName], 0); t.end(); }); + +test('#getCostumes returns the costumes', function (t) { + var spr = new Sprite(); + var a = new RenderedTarget(spr, null); + var costumes = [1, 2, 3]; + a.sprite.costumes = costumes; + t.equals(a.getCostumes(), costumes); + t.end(); +}); + +test('#getSounds returns the sounds', function (t) { + var spr = new Sprite(); + var a = new RenderedTarget(spr, null); + var sounds = [1, 2, 3]; + a.sprite.sounds = sounds; + t.equals(a.getSounds(), sounds); + t.end(); +}); + +test('#toJSON returns the sounds and costumes', function (t) { + var spr = new Sprite(); + var a = new RenderedTarget(spr, null); + var sounds = [1, 2, 3]; + var costumes = ['a', 'b', 'c']; + a.sprite.sounds = sounds; + a.sprite.costumes = costumes; + t.same(a.toJSON().sounds, sounds); + t.same(a.toJSON().costumes, costumes); + t.end(); +}); From da6ce9182d3f3493f4321672968886ac96c4c10f Mon Sep 17 00:00:00 2001 From: SillyInventor Date: Fri, 7 Apr 2017 14:50:13 -0400 Subject: [PATCH 287/721] Added testing to rendered-target Added a fake renderer with known predictable output Added testing for most if not all functions --- test/unit/sprites_rendered-target.js | 261 ++++++++++++++++++++++++++- 1 file changed, 259 insertions(+), 2 deletions(-) diff --git a/test/unit/sprites_rendered-target.js b/test/unit/sprites_rendered-target.js index 3ff40e2c..33404028 100644 --- a/test/unit/sprites_rendered-target.js +++ b/test/unit/sprites_rendered-target.js @@ -1,6 +1,66 @@ var test = require('tap').test; var RenderedTarget = require('../../src/sprites/rendered-target'); var Sprite = require('../../src/sprites/sprite'); +var Runtime = require('../../src/engine/runtime'); + + +var fakeRenderer = function (){ + var renderer = new Object(); + renderer.unused = ''; + renderer.x = 0; + renderer.y = 0; + renderer.order = 0; + renderer.spriteCount = 5; + renderer.createDrawable = function () { + return true; + }; + renderer.getFencedPositionOfDrawable = function (d, p) { // eslint-disable-line no-unused-vars + return [p[0], p[1]]; + }; + renderer.updateDrawableProperties = function (d, p) { // eslint-disable-line no-unused-vars + if (p.position) { + renderer.x = p.position[0]; + renderer.y = p.position[1]; + } + return true; + }; + renderer.getSkinSize = function (d) { // eslint-disable-line no-unused-vars + return [0, 0]; + }; + renderer.pick = function (x, y, a, b, d) { // eslint-disable-line no-unused-vars + return true; + }; + renderer.isTouchingColor = function (d, c) { // eslint-disable-line no-unused-vars + return true; + }; + renderer.setDrawableOrder = function (d, l, optA, optB) { // eslint-disable-line no-unused-vars + return true; + }; + renderer.getBounds = function (d) { // eslint-disable-line no-unused-vars + return {left: renderer.x, right: renderer.x, top: renderer.y, bottom: renderer.y}; + }; + renderer.setDrawableOrder = function (d, a, optA, optB){ // eslint-disable-line no-unused-vars + if (d === 999) return 1; // fake for test case + if (optA) { + a += renderer.order; + } + if (optB) { + a = Math.max(a, optB); + } + a = Math.max(a, 0); + renderer.order = Math.min(a, renderer.spriteCount); + return renderer.order; + }; + renderer.pick = function (x, y, a, b, c){ // eslint-disable-line no-unused-vars + return c[0]; + }; + renderer.isTouchingColor = function (a, b){ // eslint-disable-line no-unused-vars + return false; + }; + + return renderer; +}; + test('clone effects', function (t) { // Create two clones and ensure they have different graphic effect objects. @@ -12,9 +72,206 @@ test('clone effects', function (t) { t.end(); }); +test('setxy', function (t) { + var s = new Sprite(); + var r = new Runtime(); + var a = new RenderedTarget(s, r); + var renderer = fakeRenderer(); + a.renderer = renderer; + a.setXY(123, 321, true); + t.equals(a.x, 123); + t.equals(a.y, 321); + t.end(); +}); + +test('direction', function (t) { + var s = new Sprite(); + var r = new Runtime(); + var a = new RenderedTarget(s, r); + var renderer = fakeRenderer(); + a.renderer = renderer; + a.setDirection(123); + t.equals(a._getRenderedDirectionAndScale().direction, 123); + t.end(); +}); + +test('setSay', function (t) { + var s = new Sprite(); + var r = new Runtime(); + var a = new RenderedTarget(s, r); + var renderer = fakeRenderer(); + a.renderer = renderer; + a.setSay(); + a.setSay('types not specified', 'message'); + t.end(); +}); + +test('setVisible', function (t) { + var s = new Sprite(); + var r = new Runtime(); + var a = new RenderedTarget(s, r); + var renderer = fakeRenderer(); + a.renderer = renderer; + a.setVisible(true); + t.end(); +}); + +test('setSize', function (t) { + var s = new Sprite(); + var r = new Runtime(); + var a = new RenderedTarget(s, r); + var renderer = fakeRenderer(); + a.renderer = renderer; + a.setSize(123); + t.equals(a._getRenderedDirectionAndScale().scale[0], 123); + t.end(); +}); + +test('set and clear effects', function (t) { + var s = new Sprite(); + var r = new Runtime(); + var a = new RenderedTarget(s, r); + var renderer = fakeRenderer(); + a.renderer = renderer; + for (var effect in a.effects) { + a.setEffect(effect, 1); + t.equals(a.effects[effect], 1); + } + a.clearEffects(); + for (effect in a.effects) { + t.equals(a.effects[effect], 0); + } + t.end(); +}); + +test('setCostume', function (t) { + var o = new Object(); + var s = new Sprite(); + var r = new Runtime(); + s.costumes = [o]; + var a = new RenderedTarget(s, r); + var renderer = fakeRenderer(); + a.renderer = renderer; + a.setCostume(0); + t.end(); +}); + +test('setRotationStyle', function (t) { + var s = new Sprite(); + var r = new Runtime(); + var a = new RenderedTarget(s, r); + var renderer = fakeRenderer(); + a.renderer = renderer; + a.setRotationStyle(RenderedTarget.ROTATION_STYLE_NONE); + t.end(); +}); + +test('getBounds', function (t) { + var s = new Sprite(); + var r = new Runtime(); + var renderer = fakeRenderer(); + r.attachRenderer(renderer); + var a = new RenderedTarget(s, r); + a.renderer = renderer; + t.equals(a.getBounds().top, 0); + a.setXY(241, 241); + t.equals(a.getBounds().top, 241); + t.end(); +}); + +test('isTouchingPoint', function (t) { + var s = new Sprite(); + var r = new Runtime(); + var renderer = fakeRenderer(); + r.attachRenderer(renderer); + var a = new RenderedTarget(s, r); + a.renderer = renderer; + t.equals(a.isTouchingPoint(), true); + t.end(); +}); + +test('isTouchingEdge', function (t) { + var s = new Sprite(); + var r = new Runtime(); + var renderer = fakeRenderer(); + r.attachRenderer(renderer); + var a = new RenderedTarget(s, r); + a.renderer = renderer; + t.equals(a.isTouchingEdge(), false); + a.setXY(1000, 1000); + t.equals(a.isTouchingEdge(), true); + t.end(); +}); + +test('isTouchingSprite', function (t) { + var s = new Sprite(); + var r = new Runtime(); + var renderer = fakeRenderer(); + r.attachRenderer(renderer); + var a = new RenderedTarget(s, r); + a.renderer = renderer; + t.equals(a.isTouchingSprite('fake'), false); + t.end(); +}); + +test('isTouchingColor', function (t) { + var s = new Sprite(); + var r = new Runtime(); + var renderer = fakeRenderer(); + r.attachRenderer(renderer); + var a = new RenderedTarget(s, r); + a.renderer = renderer; + t.equals(a.isTouchingColor(), false); + t.end(); +}); + +test('colorIsTouchingColor', function (t) { + var s = new Sprite(); + var r = new Runtime(); + var renderer = fakeRenderer(); + r.attachRenderer(renderer); + var a = new RenderedTarget(s, r); + a.renderer = renderer; + t.equals(a.colorIsTouchingColor(), false); + t.end(); +}); + +test('layers', function (t) { + var s = new Sprite(); + var r = new Runtime(); + var renderer = fakeRenderer(); + var o = new Object(); + r.attachRenderer(renderer); + var a = new RenderedTarget(s, r); + a.renderer = renderer; + a.goToFront(); + t.equals(a.renderer.order, 5); + a.goBackLayers(2); + t.equals(a.renderer.order, 3); + o.drawableID = 999; + a.goBehindOther(o); + t.equals(a.renderer.order, 1); + t.end(); +}); + +test('keepInFence', function (t) { + var s = new Sprite(); + var r = new Runtime(); + var renderer = fakeRenderer(); + r.attachRenderer(renderer); + var a = new RenderedTarget(s, r); + a.renderer = renderer; + t.equals(a.keepInFence(1000, 1000)[0], 240); + t.equals(a.keepInFence(-1000, 1000)[0], -240); + t.equals(a.keepInFence(1000, 1000)[1], 180); + t.equals(a.keepInFence(1000, -1000)[1], -180); + t.end(); +}); + test('#stopAll clears graphics effects', function (t) { - var spr = new Sprite(); - var a = new RenderedTarget(spr, null); + var s = new Sprite(); + var r = new Runtime(); + var a = new RenderedTarget(s, r); var effectName = 'brightness'; a.setEffect(effectName, 100); a.onStopAll(); From b693d4f4efaa0580694000b04306851019bad337 Mon Sep 17 00:00:00 2001 From: Rasmus Haglund Date: Sat, 8 Apr 2017 19:35:29 +0200 Subject: [PATCH 288/721] Fix rounded get size reporting --- src/blocks/scratch3_looks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index 95eef028..ac71558d 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -201,7 +201,7 @@ Scratch3LooksBlocks.prototype.goBackLayers = function (args, util) { }; Scratch3LooksBlocks.prototype.getSize = function (args, util) { - return util.target.size; + return Math.round(util.target.size); }; Scratch3LooksBlocks.prototype.getBackdropIndex = function () { From 0402b7ed8e81abddfb1c5267057ac8e5baf7640c Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Tue, 11 Apr 2017 11:36:59 -0400 Subject: [PATCH 289/721] Standardize repository settings Set dev tool to `cheap-module-source-map`. --- webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index 0fe8af04..de0304e8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -9,7 +9,7 @@ var base = { host: '0.0.0.0', port: process.env.PORT || 8073 }, - devtool: 'source-map', + devtool: 'cheap-module-source-map', plugins: [ new webpack.optimize.UglifyJsPlugin({ include: /\.min\.js$/, From f646a61c5b0160b477edd3b8b6be55f74daacce3 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 17 Apr 2017 15:09:57 -0400 Subject: [PATCH 290/721] Use es6 lint rules --- src/.eslintrc.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/.eslintrc.js b/src/.eslintrc.js index 425bb3ff..a704f703 100644 --- a/src/.eslintrc.js +++ b/src/.eslintrc.js @@ -1,10 +1,7 @@ module.exports = { root: true, - extends: 'scratch', + extends: ['scratch', 'scratch/es6'], env: { browser: true - }, - globals: { - Promise: true } }; From e01c4ae108d7edfee61b67b7f36cc6c7978c7f5a Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 17 Apr 2017 15:10:04 -0400 Subject: [PATCH 291/721] Pass with --fix --- src/blocks/scratch3_control.js | 24 ++--- src/blocks/scratch3_data.js | 56 +++++------ src/blocks/scratch3_event.js | 18 ++-- src/blocks/scratch3_looks.js | 44 ++++----- src/blocks/scratch3_motion.js | 92 ++++++++--------- src/blocks/scratch3_operators.js | 28 +++--- src/blocks/scratch3_pen.js | 56 +++++------ src/blocks/scratch3_procedures.js | 14 +-- src/blocks/scratch3_sensing.js | 48 ++++----- src/blocks/scratch3_sound.js | 76 +++++++------- src/engine/adapter.js | 36 +++---- src/engine/blocks.js | 104 ++++++++++---------- src/engine/execute.js | 62 ++++++------ src/engine/list.js | 2 +- src/engine/mutation-adapter.js | 12 +-- src/engine/runtime.js | 158 +++++++++++++++--------------- src/engine/sequencer.js | 46 ++++----- src/engine/target.js | 22 ++--- src/engine/thread.js | 30 +++--- src/engine/variable.js | 2 +- src/import/load-costume.js | 64 ++++++------ src/import/load-sound.js | 12 +-- src/import/sb2import.js | 134 ++++++++++++------------- src/import/sb2specmap.js | 2 +- src/index.js | 2 +- src/io/clock.js | 6 +- src/io/keyboard.js | 10 +- src/io/mouse.js | 10 +- src/playground/playground.js | 130 ++++++++++++------------ src/sprites/rendered-target.js | 88 ++++++++--------- src/sprites/sprite.js | 8 +- src/util/cast.js | 24 ++--- src/util/clone.js | 2 +- src/util/color.js | 64 ++++++------ src/util/log.js | 2 +- src/util/math-util.js | 4 +- src/util/string-util.js | 6 +- src/util/timer.js | 8 +- src/util/uid.js | 12 +-- src/util/xml-escape.js | 4 +- src/virtual-machine.js | 105 +++++++++----------- 41 files changed, 803 insertions(+), 824 deletions(-) diff --git a/src/blocks/scratch3_control.js b/src/blocks/scratch3_control.js index 285b241a..1d7e8f42 100644 --- a/src/blocks/scratch3_control.js +++ b/src/blocks/scratch3_control.js @@ -1,7 +1,7 @@ -var Cast = require('../util/cast'); -var Timer = require('../util/timer'); +const Cast = require('../util/cast'); +const Timer = require('../util/timer'); -var Scratch3ControlBlocks = function (runtime) { +const Scratch3ControlBlocks = function (runtime) { /** * The runtime instantiating this block package. * @type {Runtime} @@ -37,7 +37,7 @@ Scratch3ControlBlocks.prototype.getHats = function () { }; Scratch3ControlBlocks.prototype.repeat = function (args, util) { - var times = Math.floor(Cast.toNumber(args.TIMES)); + const times = Math.floor(Cast.toNumber(args.TIMES)); // Initialize loop if (typeof util.stackFrame.loopCounter === 'undefined') { util.stackFrame.loopCounter = times; @@ -54,7 +54,7 @@ Scratch3ControlBlocks.prototype.repeat = function (args, util) { }; Scratch3ControlBlocks.prototype.repeatUntil = function (args, util) { - var condition = Cast.toBoolean(args.CONDITION); + const condition = Cast.toBoolean(args.CONDITION); // If the condition is true, start the branch. if (!condition) { util.startBranch(1, true); @@ -62,7 +62,7 @@ Scratch3ControlBlocks.prototype.repeatUntil = function (args, util) { }; Scratch3ControlBlocks.prototype.waitUntil = function (args, util) { - var condition = Cast.toBoolean(args.CONDITION); + const condition = Cast.toBoolean(args.CONDITION); if (!condition) { util.yield(); } @@ -79,7 +79,7 @@ Scratch3ControlBlocks.prototype.wait = function (args, util) { util.yield(); this.runtime.requestRedraw(); } else { - var duration = Math.max(0, 1000 * Cast.toNumber(args.DURATION)); + const duration = Math.max(0, 1000 * Cast.toNumber(args.DURATION)); if (util.stackFrame.timer.timeElapsed() < duration) { util.yield(); } @@ -87,14 +87,14 @@ Scratch3ControlBlocks.prototype.wait = function (args, util) { }; Scratch3ControlBlocks.prototype.if = function (args, util) { - var condition = Cast.toBoolean(args.CONDITION); + const condition = Cast.toBoolean(args.CONDITION); if (condition) { util.startBranch(1, false); } }; Scratch3ControlBlocks.prototype.ifElse = function (args, util) { - var condition = Cast.toBoolean(args.CONDITION); + const condition = Cast.toBoolean(args.CONDITION); if (condition) { util.startBranch(1, false); } else { @@ -103,7 +103,7 @@ Scratch3ControlBlocks.prototype.ifElse = function (args, util) { }; Scratch3ControlBlocks.prototype.stop = function (args, util) { - var option = args.STOP_OPTION; + const option = args.STOP_OPTION; if (option === 'all') { util.stopAll(); } else if (option === 'other scripts in sprite' || @@ -115,7 +115,7 @@ Scratch3ControlBlocks.prototype.stop = function (args, util) { }; Scratch3ControlBlocks.prototype.createClone = function (args, util) { - var cloneTarget; + let cloneTarget; if (args.CLONE_OPTION === '_myself_') { cloneTarget = util.target; } else { @@ -124,7 +124,7 @@ Scratch3ControlBlocks.prototype.createClone = function (args, util) { if (!cloneTarget) { return; } - var newClone = cloneTarget.makeClone(); + const newClone = cloneTarget.makeClone(); if (newClone) { this.runtime.targets.push(newClone); } diff --git a/src/blocks/scratch3_data.js b/src/blocks/scratch3_data.js index b1783563..3b3b0613 100644 --- a/src/blocks/scratch3_data.js +++ b/src/blocks/scratch3_data.js @@ -1,6 +1,6 @@ -var Cast = require('../util/cast'); +const Cast = require('../util/cast'); -var Scratch3DataBlocks = function (runtime) { +const Scratch3DataBlocks = function (runtime) { /** * The runtime instantiating this block package. * @type {Runtime} @@ -29,30 +29,30 @@ Scratch3DataBlocks.prototype.getPrimitives = function () { }; Scratch3DataBlocks.prototype.getVariable = function (args, util) { - var variable = util.target.lookupOrCreateVariable(args.VARIABLE); + const variable = util.target.lookupOrCreateVariable(args.VARIABLE); return variable.value; }; Scratch3DataBlocks.prototype.setVariableTo = function (args, util) { - var variable = util.target.lookupOrCreateVariable(args.VARIABLE); + const variable = util.target.lookupOrCreateVariable(args.VARIABLE); variable.value = args.VALUE; }; Scratch3DataBlocks.prototype.changeVariableBy = function (args, util) { - var variable = util.target.lookupOrCreateVariable(args.VARIABLE); - var castedValue = Cast.toNumber(variable.value); - var dValue = Cast.toNumber(args.VALUE); + const variable = util.target.lookupOrCreateVariable(args.VARIABLE); + const castedValue = Cast.toNumber(variable.value); + const dValue = Cast.toNumber(args.VALUE); variable.value = castedValue + dValue; }; Scratch3DataBlocks.prototype.getListContents = function (args, util) { - var list = util.target.lookupOrCreateList(args.LIST); + const list = util.target.lookupOrCreateList(args.LIST); // Determine if the list is all single letters. // If it is, report contents joined together with no separator. // If it's not, report contents joined together with a space. - var allSingleLetters = true; - for (var i = 0; i < list.contents.length; i++) { - var listItem = list.contents[i]; + let allSingleLetters = true; + for (let i = 0; i < list.contents.length; i++) { + const listItem = list.contents[i]; if (!((typeof listItem === 'string') && (listItem.length === 1))) { allSingleLetters = false; @@ -61,19 +61,19 @@ Scratch3DataBlocks.prototype.getListContents = function (args, util) { } if (allSingleLetters) { return list.contents.join(''); - } else { - return list.contents.join(' '); } + return list.contents.join(' '); + }; Scratch3DataBlocks.prototype.addToList = function (args, util) { - var list = util.target.lookupOrCreateList(args.LIST); + const list = util.target.lookupOrCreateList(args.LIST); list.contents.push(args.ITEM); }; Scratch3DataBlocks.prototype.deleteOfList = function (args, util) { - var list = util.target.lookupOrCreateList(args.LIST); - var index = Cast.toListIndex(args.INDEX, list.contents.length); + const list = util.target.lookupOrCreateList(args.LIST); + const index = Cast.toListIndex(args.INDEX, list.contents.length); if (index === Cast.LIST_INVALID) { return; } else if (index === Cast.LIST_ALL) { @@ -84,9 +84,9 @@ Scratch3DataBlocks.prototype.deleteOfList = function (args, util) { }; Scratch3DataBlocks.prototype.insertAtList = function (args, util) { - var item = args.ITEM; - var list = util.target.lookupOrCreateList(args.LIST); - var index = Cast.toListIndex(args.INDEX, list.contents.length + 1); + const item = args.ITEM; + const list = util.target.lookupOrCreateList(args.LIST); + const index = Cast.toListIndex(args.INDEX, list.contents.length + 1); if (index === Cast.LIST_INVALID) { return; } @@ -94,9 +94,9 @@ Scratch3DataBlocks.prototype.insertAtList = function (args, util) { }; Scratch3DataBlocks.prototype.replaceItemOfList = function (args, util) { - var item = args.ITEM; - var list = util.target.lookupOrCreateList(args.LIST); - var index = Cast.toListIndex(args.INDEX, list.contents.length); + const item = args.ITEM; + const list = util.target.lookupOrCreateList(args.LIST); + const index = Cast.toListIndex(args.INDEX, list.contents.length); if (index === Cast.LIST_INVALID) { return; } @@ -104,8 +104,8 @@ Scratch3DataBlocks.prototype.replaceItemOfList = function (args, util) { }; Scratch3DataBlocks.prototype.getItemOfList = function (args, util) { - var list = util.target.lookupOrCreateList(args.LIST); - var index = Cast.toListIndex(args.INDEX, list.contents.length); + const list = util.target.lookupOrCreateList(args.LIST); + const index = Cast.toListIndex(args.INDEX, list.contents.length); if (index === Cast.LIST_INVALID) { return ''; } @@ -113,19 +113,19 @@ Scratch3DataBlocks.prototype.getItemOfList = function (args, util) { }; Scratch3DataBlocks.prototype.lengthOfList = function (args, util) { - var list = util.target.lookupOrCreateList(args.LIST); + const list = util.target.lookupOrCreateList(args.LIST); return list.contents.length; }; Scratch3DataBlocks.prototype.listContainsItem = function (args, util) { - var item = args.ITEM; - var list = util.target.lookupOrCreateList(args.LIST); + const item = args.ITEM; + const list = util.target.lookupOrCreateList(args.LIST); if (list.contents.indexOf(item) >= 0) { return true; } // Try using Scratch comparison operator on each item. // (Scratch considers the string '123' equal to the number 123). - for (var i = 0; i < list.contents.length; i++) { + for (let i = 0; i < list.contents.length; i++) { if (Cast.compare(list.contents[i], item) === 0) { return true; } diff --git a/src/blocks/scratch3_event.js b/src/blocks/scratch3_event.js index b4c40116..62d65fa1 100644 --- a/src/blocks/scratch3_event.js +++ b/src/blocks/scratch3_event.js @@ -1,6 +1,6 @@ -var Cast = require('../util/cast'); +const Cast = require('../util/cast'); -var Scratch3EventBlocks = function (runtime) { +const Scratch3EventBlocks = function (runtime) { /** * The runtime instantiating this block package. * @type {Runtime} @@ -45,8 +45,8 @@ Scratch3EventBlocks.prototype.getHats = function () { }; Scratch3EventBlocks.prototype.hatGreaterThanPredicate = function (args, util) { - var option = Cast.toString(args.WHENGREATERTHANMENU).toLowerCase(); - var value = Cast.toNumber(args.VALUE); + const option = Cast.toString(args.WHENGREATERTHANMENU).toLowerCase(); + const value = Cast.toNumber(args.VALUE); // @todo: Other cases :) if (option === 'timer') { return util.ioQuery('clock', 'projectTimer') > value; @@ -55,14 +55,14 @@ Scratch3EventBlocks.prototype.hatGreaterThanPredicate = function (args, util) { }; Scratch3EventBlocks.prototype.broadcast = function (args, util) { - var broadcastOption = Cast.toString(args.BROADCAST_OPTION); + const broadcastOption = Cast.toString(args.BROADCAST_OPTION); util.startHats('event_whenbroadcastreceived', { BROADCAST_OPTION: broadcastOption }); }; Scratch3EventBlocks.prototype.broadcastAndWait = function (args, util) { - var broadcastOption = Cast.toString(args.BROADCAST_OPTION); + const broadcastOption = Cast.toString(args.BROADCAST_OPTION); // Have we run before, starting threads? if (!util.stackFrame.startedThreads) { // No - start hats for this broadcast. @@ -77,10 +77,8 @@ Scratch3EventBlocks.prototype.broadcastAndWait = function (args, util) { } } // We've run before; check if the wait is still going on. - var instance = this; - var waiting = util.stackFrame.startedThreads.some(function (thread) { - return instance.runtime.isActiveThread(thread); - }); + const instance = this; + const waiting = util.stackFrame.startedThreads.some(thread => instance.runtime.isActiveThread(thread)); if (waiting) { util.yield(); } diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index ac71558d..e0807337 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -1,6 +1,6 @@ -var Cast = require('../util/cast'); +const Cast = require('../util/cast'); -var Scratch3LooksBlocks = function (runtime) { +const Scratch3LooksBlocks = function (runtime) { /** * The runtime instantiating this block package. * @type {Runtime} @@ -45,8 +45,8 @@ Scratch3LooksBlocks.prototype.say = function (args, util) { Scratch3LooksBlocks.prototype.sayforsecs = function (args, util) { util.target.setSay('say', args.MESSAGE); - return new Promise(function (resolve) { - setTimeout(function () { + return new Promise(resolve => { + setTimeout(() => { // Clear say bubble and proceed. util.target.setSay(); resolve(); @@ -60,8 +60,8 @@ Scratch3LooksBlocks.prototype.think = function (args, util) { Scratch3LooksBlocks.prototype.thinkforsecs = function (args, util) { util.target.setSay('think', args.MESSAGE); - return new Promise(function (resolve) { - setTimeout(function () { + return new Promise(resolve => { + setTimeout(() => { // Clear say bubble and proceed. util.target.setSay(); resolve(); @@ -91,7 +91,7 @@ Scratch3LooksBlocks.prototype._setCostumeOrBackdrop = function (target, target.setCostume(optZeroIndex ? requestedCostume : requestedCostume - 1); } else { - var costumeIndex = target.getCostumeIndexByName(requestedCostume); + const costumeIndex = target.getCostumeIndexByName(requestedCostume); if (costumeIndex > -1) { target.setCostume(costumeIndex); } else if (requestedCostume === 'previous costume' || @@ -101,7 +101,7 @@ Scratch3LooksBlocks.prototype._setCostumeOrBackdrop = function (target, requestedCostume === 'next backdrop') { target.setCostume(target.currentCostume + 1); } else { - var forcedNumber = Number(requestedCostume); + const forcedNumber = Number(requestedCostume); if (!isNaN(forcedNumber)) { target.setCostume(optZeroIndex ? forcedNumber : forcedNumber - 1); @@ -110,7 +110,7 @@ Scratch3LooksBlocks.prototype._setCostumeOrBackdrop = function (target, } if (target === this.runtime.getTargetForStage()) { // Target is the stage - start hats. - var newName = target.sprite.costumes[target.currentCostume].name; + const newName = target.sprite.costumes[target.currentCostume].name; return this.runtime.startHats('event_whenbackdropswitchesto', { BACKDROP: newName }); @@ -148,33 +148,31 @@ Scratch3LooksBlocks.prototype.switchBackdropAndWait = function (args, util) { } } // We've run before; check if the wait is still going on. - var instance = this; - var waiting = util.stackFrame.startedThreads.some(function (thread) { - return instance.runtime.isActiveThread(thread); - }); + const instance = this; + const waiting = util.stackFrame.startedThreads.some(thread => instance.runtime.isActiveThread(thread)); if (waiting) { util.yield(); } }; Scratch3LooksBlocks.prototype.nextBackdrop = function () { - var stage = this.runtime.getTargetForStage(); + const stage = this.runtime.getTargetForStage(); this._setCostumeOrBackdrop( stage, stage.currentCostume + 1, true ); }; Scratch3LooksBlocks.prototype.changeEffect = function (args, util) { - var effect = Cast.toString(args.EFFECT).toLowerCase(); - var change = Cast.toNumber(args.CHANGE); + const effect = Cast.toString(args.EFFECT).toLowerCase(); + const change = Cast.toNumber(args.CHANGE); if (!util.target.effects.hasOwnProperty(effect)) return; - var newValue = change + util.target.effects[effect]; + const newValue = change + util.target.effects[effect]; util.target.setEffect(effect, newValue); }; Scratch3LooksBlocks.prototype.setEffect = function (args, util) { - var effect = Cast.toString(args.EFFECT).toLowerCase(); - var value = Cast.toNumber(args.VALUE); + const effect = Cast.toString(args.EFFECT).toLowerCase(); + const value = Cast.toNumber(args.VALUE); util.target.setEffect(effect, value); }; @@ -183,12 +181,12 @@ Scratch3LooksBlocks.prototype.clearEffects = function (args, util) { }; Scratch3LooksBlocks.prototype.changeSize = function (args, util) { - var change = Cast.toNumber(args.CHANGE); + const change = Cast.toNumber(args.CHANGE); util.target.setSize(util.target.size + change); }; Scratch3LooksBlocks.prototype.setSize = function (args, util) { - var size = Cast.toNumber(args.SIZE); + const size = Cast.toNumber(args.SIZE); util.target.setSize(size); }; @@ -205,12 +203,12 @@ Scratch3LooksBlocks.prototype.getSize = function (args, util) { }; Scratch3LooksBlocks.prototype.getBackdropIndex = function () { - var stage = this.runtime.getTargetForStage(); + const stage = this.runtime.getTargetForStage(); return stage.currentCostume + 1; }; Scratch3LooksBlocks.prototype.getBackdropName = function () { - var stage = this.runtime.getTargetForStage(); + const stage = this.runtime.getTargetForStage(); return stage.sprite.costumes[stage.currentCostume].name; }; diff --git a/src/blocks/scratch3_motion.js b/src/blocks/scratch3_motion.js index 857644dd..c70eb3bd 100644 --- a/src/blocks/scratch3_motion.js +++ b/src/blocks/scratch3_motion.js @@ -1,8 +1,8 @@ -var Cast = require('../util/cast'); -var MathUtil = require('../util/math-util'); -var Timer = require('../util/timer'); +const Cast = require('../util/cast'); +const MathUtil = require('../util/math-util'); +const Timer = require('../util/timer'); -var Scratch3MotionBlocks = function (runtime) { +const Scratch3MotionBlocks = function (runtime) { /** * The runtime instantiating this block package. * @type {Runtime} @@ -37,32 +37,32 @@ Scratch3MotionBlocks.prototype.getPrimitives = function () { }; Scratch3MotionBlocks.prototype.moveSteps = function (args, util) { - var steps = Cast.toNumber(args.STEPS); - var radians = MathUtil.degToRad(90 - util.target.direction); - var dx = steps * Math.cos(radians); - var dy = steps * Math.sin(radians); + const steps = Cast.toNumber(args.STEPS); + const radians = MathUtil.degToRad(90 - util.target.direction); + const dx = steps * Math.cos(radians); + const dy = steps * Math.sin(radians); util.target.setXY(util.target.x + dx, util.target.y + dy); }; Scratch3MotionBlocks.prototype.goToXY = function (args, util) { - var x = Cast.toNumber(args.X); - var y = Cast.toNumber(args.Y); + const x = Cast.toNumber(args.X); + const y = Cast.toNumber(args.Y); util.target.setXY(x, y); }; Scratch3MotionBlocks.prototype.goTo = function (args, util) { - var targetX = 0; - var targetY = 0; + let targetX = 0; + let targetY = 0; if (args.TO === '_mouse_') { targetX = util.ioQuery('mouse', 'getX'); targetY = util.ioQuery('mouse', 'getY'); } else if (args.TO === '_random_') { - var stageWidth = this.runtime.constructor.STAGE_WIDTH; - var stageHeight = this.runtime.constructor.STAGE_HEIGHT; + const stageWidth = this.runtime.constructor.STAGE_WIDTH; + const stageHeight = this.runtime.constructor.STAGE_HEIGHT; targetX = Math.round(stageWidth * (Math.random() - 0.5)); targetY = Math.round(stageHeight * (Math.random() - 0.5)); } else { - var goToTarget = this.runtime.getSpriteTargetByName(args.TO); + const goToTarget = this.runtime.getSpriteTargetByName(args.TO); if (!goToTarget) return; targetX = goToTarget.x; targetY = goToTarget.y; @@ -71,36 +71,36 @@ Scratch3MotionBlocks.prototype.goTo = function (args, util) { }; Scratch3MotionBlocks.prototype.turnRight = function (args, util) { - var degrees = Cast.toNumber(args.DEGREES); + const degrees = Cast.toNumber(args.DEGREES); util.target.setDirection(util.target.direction + degrees); }; Scratch3MotionBlocks.prototype.turnLeft = function (args, util) { - var degrees = Cast.toNumber(args.DEGREES); + const degrees = Cast.toNumber(args.DEGREES); util.target.setDirection(util.target.direction - degrees); }; Scratch3MotionBlocks.prototype.pointInDirection = function (args, util) { - var direction = Cast.toNumber(args.DIRECTION); + const direction = Cast.toNumber(args.DIRECTION); util.target.setDirection(direction); }; Scratch3MotionBlocks.prototype.pointTowards = function (args, util) { - var targetX = 0; - var targetY = 0; + let targetX = 0; + let targetY = 0; if (args.TOWARDS === '_mouse_') { targetX = util.ioQuery('mouse', 'getX'); targetY = util.ioQuery('mouse', 'getY'); } else { - var pointTarget = this.runtime.getSpriteTargetByName(args.TOWARDS); + const pointTarget = this.runtime.getSpriteTargetByName(args.TOWARDS); if (!pointTarget) return; targetX = pointTarget.x; targetY = pointTarget.y; } - var dx = targetX - util.target.x; - var dy = targetY - util.target.y; - var direction = 90 - MathUtil.radToDeg(Math.atan2(dy, dx)); + const dx = targetX - util.target.x; + const dy = targetY - util.target.y; + const direction = 90 - MathUtil.radToDeg(Math.atan2(dy, dx)); util.target.setDirection(direction); }; @@ -121,12 +121,12 @@ Scratch3MotionBlocks.prototype.glide = function (args, util) { } util.yield(); } else { - var timeElapsed = util.stackFrame.timer.timeElapsed(); + const timeElapsed = util.stackFrame.timer.timeElapsed(); if (timeElapsed < util.stackFrame.duration * 1000) { // In progress: move to intermediate position. - var frac = timeElapsed / (util.stackFrame.duration * 1000); - var dx = frac * (util.stackFrame.endX - util.stackFrame.startX); - var dy = frac * (util.stackFrame.endY - util.stackFrame.startY); + const frac = timeElapsed / (util.stackFrame.duration * 1000); + const dx = frac * (util.stackFrame.endX - util.stackFrame.startX); + const dy = frac * (util.stackFrame.endY - util.stackFrame.startY); util.target.setXY( util.stackFrame.startX + dx, util.stackFrame.startY + dy @@ -140,22 +140,22 @@ Scratch3MotionBlocks.prototype.glide = function (args, util) { }; Scratch3MotionBlocks.prototype.ifOnEdgeBounce = function (args, util) { - var bounds = util.target.getBounds(); + const bounds = util.target.getBounds(); if (!bounds) { return; } // Measure distance to edges. // Values are positive when the sprite is far away, // and clamped to zero when the sprite is beyond. - var stageWidth = this.runtime.constructor.STAGE_WIDTH; - var stageHeight = this.runtime.constructor.STAGE_HEIGHT; - var distLeft = Math.max(0, (stageWidth / 2) + bounds.left); - var distTop = Math.max(0, (stageHeight / 2) - bounds.top); - var distRight = Math.max(0, (stageWidth / 2) - bounds.right); - var distBottom = Math.max(0, (stageHeight / 2) + bounds.bottom); + const stageWidth = this.runtime.constructor.STAGE_WIDTH; + const stageHeight = this.runtime.constructor.STAGE_HEIGHT; + const distLeft = Math.max(0, (stageWidth / 2) + bounds.left); + const distTop = Math.max(0, (stageHeight / 2) - bounds.top); + const distRight = Math.max(0, (stageWidth / 2) - bounds.right); + const distBottom = Math.max(0, (stageHeight / 2) + bounds.bottom); // Find the nearest edge. - var nearestEdge = ''; - var minDist = Infinity; + let nearestEdge = ''; + let minDist = Infinity; if (distLeft < minDist) { minDist = distLeft; nearestEdge = 'left'; @@ -176,9 +176,9 @@ Scratch3MotionBlocks.prototype.ifOnEdgeBounce = function (args, util) { return; // Not touching any edge. } // Point away from the nearest edge. - var radians = MathUtil.degToRad(90 - util.target.direction); - var dx = Math.cos(radians); - var dy = -Math.sin(radians); + const radians = MathUtil.degToRad(90 - util.target.direction); + let dx = Math.cos(radians); + let dy = -Math.sin(radians); if (nearestEdge === 'left') { dx = Math.max(0.2, Math.abs(dx)); } else if (nearestEdge === 'top') { @@ -188,10 +188,10 @@ Scratch3MotionBlocks.prototype.ifOnEdgeBounce = function (args, util) { } else if (nearestEdge === 'bottom') { dy = 0 - Math.max(0.2, Math.abs(dy)); } - var newDirection = MathUtil.radToDeg(Math.atan2(dy, dx)) + 90; + const newDirection = MathUtil.radToDeg(Math.atan2(dy, dx)) + 90; util.target.setDirection(newDirection); // Keep within the stage. - var fencedPosition = util.target.keepInFence(util.target.x, util.target.y); + const fencedPosition = util.target.keepInFence(util.target.x, util.target.y); util.target.setXY(fencedPosition[0], fencedPosition[1]); }; @@ -200,22 +200,22 @@ Scratch3MotionBlocks.prototype.setRotationStyle = function (args, util) { }; Scratch3MotionBlocks.prototype.changeX = function (args, util) { - var dx = Cast.toNumber(args.DX); + const dx = Cast.toNumber(args.DX); util.target.setXY(util.target.x + dx, util.target.y); }; Scratch3MotionBlocks.prototype.setX = function (args, util) { - var x = Cast.toNumber(args.X); + const x = Cast.toNumber(args.X); util.target.setXY(x, util.target.y); }; Scratch3MotionBlocks.prototype.changeY = function (args, util) { - var dy = Cast.toNumber(args.DY); + const dy = Cast.toNumber(args.DY); util.target.setXY(util.target.x, util.target.y + dy); }; Scratch3MotionBlocks.prototype.setY = function (args, util) { - var y = Cast.toNumber(args.Y); + const y = Cast.toNumber(args.Y); util.target.setXY(util.target.x, y); }; diff --git a/src/blocks/scratch3_operators.js b/src/blocks/scratch3_operators.js index fce7fb3a..54a983c9 100644 --- a/src/blocks/scratch3_operators.js +++ b/src/blocks/scratch3_operators.js @@ -1,7 +1,7 @@ -var Cast = require('../util/cast.js'); -var MathUtil = require('../util/math-util.js'); +const Cast = require('../util/cast.js'); +const MathUtil = require('../util/math-util.js'); -var Scratch3OperatorsBlocks = function (runtime) { +const Scratch3OperatorsBlocks = function (runtime) { /** * The runtime instantiating this block package. * @type {Runtime} @@ -76,10 +76,10 @@ Scratch3OperatorsBlocks.prototype.not = function (args) { }; Scratch3OperatorsBlocks.prototype.random = function (args) { - var nFrom = Cast.toNumber(args.FROM); - var nTo = Cast.toNumber(args.TO); - var low = nFrom <= nTo ? nFrom : nTo; - var high = nFrom <= nTo ? nTo : nFrom; + const nFrom = Cast.toNumber(args.FROM); + const nTo = Cast.toNumber(args.TO); + const low = nFrom <= nTo ? nFrom : nTo; + const high = nFrom <= nTo ? nTo : nFrom; if (low === high) return low; // If both arguments are ints, truncate the result to an int. if (Cast.isInt(args.FROM) && Cast.isInt(args.TO)) { @@ -93,8 +93,8 @@ Scratch3OperatorsBlocks.prototype.join = function (args) { }; Scratch3OperatorsBlocks.prototype.letterOf = function (args) { - var index = Cast.toNumber(args.LETTER) - 1; - var str = Cast.toString(args.STRING); + const index = Cast.toNumber(args.LETTER) - 1; + const str = Cast.toString(args.STRING); // Out of bounds? if (index < 0 || index >= str.length) { return ''; @@ -107,9 +107,9 @@ Scratch3OperatorsBlocks.prototype.length = function (args) { }; Scratch3OperatorsBlocks.prototype.mod = function (args) { - var n = Cast.toNumber(args.NUM1); - var modulus = Cast.toNumber(args.NUM2); - var result = n % modulus; + const n = Cast.toNumber(args.NUM1); + const modulus = Cast.toNumber(args.NUM2); + let result = n % modulus; // Scratch mod is kept positive. if (result / modulus < 0) result += modulus; return result; @@ -120,8 +120,8 @@ Scratch3OperatorsBlocks.prototype.round = function (args) { }; Scratch3OperatorsBlocks.prototype.mathop = function (args) { - var operator = Cast.toString(args.OPERATOR).toLowerCase(); - var n = Cast.toNumber(args.NUM); + const operator = Cast.toString(args.OPERATOR).toLowerCase(); + const n = Cast.toNumber(args.NUM); switch (operator) { case 'abs': return Math.abs(n); case 'floor': return Math.floor(n); diff --git a/src/blocks/scratch3_pen.js b/src/blocks/scratch3_pen.js index 9b9f6491..4f59492d 100644 --- a/src/blocks/scratch3_pen.js +++ b/src/blocks/scratch3_pen.js @@ -1,8 +1,8 @@ -var Cast = require('../util/cast'); -var Clone = require('../util/clone'); -var Color = require('../util/color'); -var MathUtil = require('../util/math-util'); -var RenderedTarget = require('../sprites/rendered-target'); +const Cast = require('../util/cast'); +const Clone = require('../util/clone'); +const Color = require('../util/color'); +const MathUtil = require('../util/math-util'); +const RenderedTarget = require('../sprites/rendered-target'); /** * @typedef {object} PenState - the pen state associated with a particular target. @@ -18,7 +18,7 @@ var RenderedTarget = require('../sprites/rendered-target'); * @param {Runtime} runtime - the runtime instantiating this block package. * @constructor */ -var Scratch3PenBlocks = function (runtime) { +const Scratch3PenBlocks = function (runtime) { /** * The runtime instantiating this block package. * @type {Runtime} @@ -107,7 +107,7 @@ Scratch3PenBlocks.prototype._getPenLayerID = function () { * @private */ Scratch3PenBlocks.prototype._getPenState = function (target) { - var penState = target.getCustomState(Scratch3PenBlocks.STATE_KEY); + let penState = target.getCustomState(Scratch3PenBlocks.STATE_KEY); if (!penState) { penState = Clone.simple(Scratch3PenBlocks.DEFAULT_PEN_STATE); target.setCustomState(Scratch3PenBlocks.STATE_KEY, penState); @@ -123,9 +123,9 @@ Scratch3PenBlocks.prototype._getPenState = function (target) { * @private */ Scratch3PenBlocks.prototype._onTargetMoved = function (target, oldX, oldY) { - var penSkinId = this._getPenLayerID(); + const penSkinId = this._getPenLayerID(); if (penSkinId >= 0) { - var penState = this._getPenState(target); + const penState = this._getPenState(target); this.runtime.renderer.penLine(penSkinId, penState.penAttributes, oldX, oldY, target.x, target.y); this.runtime.requestRedraw(); } @@ -137,8 +137,8 @@ Scratch3PenBlocks.prototype._onTargetMoved = function (target, oldX, oldY) { * @private */ Scratch3PenBlocks.prototype._updatePenColor = function (penState) { - var rgb = Color.hsvToRgb({h: penState.hue * 180 / 100, s: 1, v: 1}); - var shade = (penState.shade > 100) ? 200 - penState.shade : penState.shade; + let rgb = Color.hsvToRgb({h: penState.hue * 180 / 100, s: 1, v: 1}); + const shade = (penState.shade > 100) ? 200 - penState.shade : penState.shade; if (shade < 50) { rgb = Color.mixRgb(Color.RGB_BLACK, rgb, (10 + shade) / 60); } else { @@ -186,7 +186,7 @@ Scratch3PenBlocks.prototype.getPrimitives = function () { * The pen "clear" block clears the pen layer's contents. */ Scratch3PenBlocks.prototype.clear = function () { - var penSkinId = this._getPenLayerID(); + const penSkinId = this._getPenLayerID(); if (penSkinId >= 0) { this.runtime.renderer.penClear(penSkinId); this.runtime.requestRedraw(); @@ -199,9 +199,9 @@ Scratch3PenBlocks.prototype.clear = function () { * @param {object} util - utility object provided by the runtime. */ Scratch3PenBlocks.prototype.stamp = function (args, util) { - var penSkinId = this._getPenLayerID(); + const penSkinId = this._getPenLayerID(); if (penSkinId >= 0) { - var target = util.target; + const target = util.target; this.runtime.renderer.penStamp(penSkinId, target.drawableID); this.runtime.requestRedraw(); } @@ -213,15 +213,15 @@ Scratch3PenBlocks.prototype.stamp = function (args, util) { * @param {object} util - utility object provided by the runtime. */ Scratch3PenBlocks.prototype.penDown = function (args, util) { - var target = util.target; - var penState = this._getPenState(target); + const target = util.target; + const penState = this._getPenState(target); if (!penState.penDown) { penState.penDown = true; target.addListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved); } - var penSkinId = this._getPenLayerID(); + const penSkinId = this._getPenLayerID(); if (penSkinId >= 0) { this.runtime.renderer.penPoint(penSkinId, penState.penAttributes, target.x, target.y); this.runtime.requestRedraw(); @@ -234,8 +234,8 @@ Scratch3PenBlocks.prototype.penDown = function (args, util) { * @param {object} util - utility object provided by the runtime. */ Scratch3PenBlocks.prototype.penUp = function (args, util) { - var target = util.target; - var penState = this._getPenState(target); + const target = util.target; + const penState = this._getPenState(target); if (penState.penDown) { penState.penDown = false; @@ -250,9 +250,9 @@ Scratch3PenBlocks.prototype.penUp = function (args, util) { * @param {object} util - utility object provided by the runtime. */ Scratch3PenBlocks.prototype.setPenColorToColor = function (args, util) { - var penState = this._getPenState(util.target); - var rgb = Cast.toRgbColorObject(args.COLOR); - var hsv = Color.rgbToHsv(rgb); + const penState = this._getPenState(util.target); + const rgb = Cast.toRgbColorObject(args.COLOR); + const hsv = Color.rgbToHsv(rgb); penState.hue = 200 * hsv.h / 360; penState.shade = 50 * hsv.v; @@ -273,7 +273,7 @@ Scratch3PenBlocks.prototype.setPenColorToColor = function (args, util) { * @param {object} util - utility object provided by the runtime. */ Scratch3PenBlocks.prototype.changePenHueBy = function (args, util) { - var penState = this._getPenState(util.target); + const penState = this._getPenState(util.target); penState.hue = this._wrapHueOrShade(penState.hue + Cast.toNumber(args.COLOR)); this._updatePenColor(penState); }; @@ -285,7 +285,7 @@ Scratch3PenBlocks.prototype.changePenHueBy = function (args, util) { * @param {object} util - utility object provided by the runtime. */ Scratch3PenBlocks.prototype.setPenHueToNumber = function (args, util) { - var penState = this._getPenState(util.target); + const penState = this._getPenState(util.target); penState.hue = this._wrapHueOrShade(Cast.toNumber(args.COLOR)); this._updatePenColor(penState); }; @@ -297,7 +297,7 @@ Scratch3PenBlocks.prototype.setPenHueToNumber = function (args, util) { * @param {object} util - utility object provided by the runtime. */ Scratch3PenBlocks.prototype.changePenShadeBy = function (args, util) { - var penState = this._getPenState(util.target); + const penState = this._getPenState(util.target); penState.shade = this._wrapHueOrShade(penState.shade + Cast.toNumber(args.SHADE)); this._updatePenColor(penState); }; @@ -309,7 +309,7 @@ Scratch3PenBlocks.prototype.changePenShadeBy = function (args, util) { * @param {object} util - utility object provided by the runtime. */ Scratch3PenBlocks.prototype.setPenShadeToNumber = function (args, util) { - var penState = this._getPenState(util.target); + const penState = this._getPenState(util.target); penState.shade = this._wrapHueOrShade(Cast.toNumber(args.SHADE)); this._updatePenColor(penState); }; @@ -321,7 +321,7 @@ Scratch3PenBlocks.prototype.setPenShadeToNumber = function (args, util) { * @param {object} util - utility object provided by the runtime. */ Scratch3PenBlocks.prototype.changePenSizeBy = function (args, util) { - var penAttributes = this._getPenState(util.target).penAttributes; + const penAttributes = this._getPenState(util.target).penAttributes; penAttributes.diameter = this._clampPenSize(penAttributes.diameter + Cast.toNumber(args.SIZE)); }; @@ -332,7 +332,7 @@ Scratch3PenBlocks.prototype.changePenSizeBy = function (args, util) { * @param {object} util - utility object provided by the runtime. */ Scratch3PenBlocks.prototype.setPenSizeTo = function (args, util) { - var penAttributes = this._getPenState(util.target).penAttributes; + const penAttributes = this._getPenState(util.target).penAttributes; penAttributes.diameter = this._clampPenSize(Cast.toNumber(args.SIZE)); }; diff --git a/src/blocks/scratch3_procedures.js b/src/blocks/scratch3_procedures.js index 471205f4..549ecb3c 100644 --- a/src/blocks/scratch3_procedures.js +++ b/src/blocks/scratch3_procedures.js @@ -1,4 +1,4 @@ -var Scratch3ProcedureBlocks = function (runtime) { +const Scratch3ProcedureBlocks = function (runtime) { /** * The runtime instantiating this block package. * @type {Runtime} @@ -24,11 +24,11 @@ Scratch3ProcedureBlocks.prototype.defNoReturn = function () { Scratch3ProcedureBlocks.prototype.callNoReturn = function (args, util) { if (!util.stackFrame.executed) { - var procedureCode = args.mutation.proccode; - var paramNames = util.getProcedureParamNames(procedureCode); - for (var i = 0; i < paramNames.length; i++) { - if (args.hasOwnProperty('input' + i)) { - util.pushParam(paramNames[i], args['input' + i]); + const procedureCode = args.mutation.proccode; + const paramNames = util.getProcedureParamNames(procedureCode); + for (let i = 0; i < paramNames.length; i++) { + if (args.hasOwnProperty(`input${i}`)) { + util.pushParam(paramNames[i], args[`input${i}`]); } } util.stackFrame.executed = true; @@ -37,7 +37,7 @@ Scratch3ProcedureBlocks.prototype.callNoReturn = function (args, util) { }; Scratch3ProcedureBlocks.prototype.param = function (args, util) { - var value = util.getParam(args.mutation.paramname); + const value = util.getParam(args.mutation.paramname); return value; }; diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index 8c43e763..27234284 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -1,6 +1,6 @@ -var Cast = require('../util/cast'); +const Cast = require('../util/cast'); -var Scratch3SensingBlocks = function (runtime) { +const Scratch3SensingBlocks = function (runtime) { /** * The runtime instantiating this block package. * @type {Runtime} @@ -32,39 +32,39 @@ Scratch3SensingBlocks.prototype.getPrimitives = function () { }; Scratch3SensingBlocks.prototype.touchingObject = function (args, util) { - var requestedObject = args.TOUCHINGOBJECTMENU; + const requestedObject = args.TOUCHINGOBJECTMENU; if (requestedObject === '_mouse_') { - var mouseX = util.ioQuery('mouse', 'getX'); - var mouseY = util.ioQuery('mouse', 'getY'); + const mouseX = util.ioQuery('mouse', 'getX'); + const mouseY = util.ioQuery('mouse', 'getY'); return util.target.isTouchingPoint(mouseX, mouseY); } else if (requestedObject === '_edge_') { return util.target.isTouchingEdge(); - } else { - return util.target.isTouchingSprite(requestedObject); } + return util.target.isTouchingSprite(requestedObject); + }; Scratch3SensingBlocks.prototype.touchingColor = function (args, util) { - var color = Cast.toRgbColorList(args.COLOR); + const color = Cast.toRgbColorList(args.COLOR); return util.target.isTouchingColor(color); }; Scratch3SensingBlocks.prototype.colorTouchingColor = function (args, util) { - var maskColor = Cast.toRgbColorList(args.COLOR); - var targetColor = Cast.toRgbColorList(args.COLOR2); + const maskColor = Cast.toRgbColorList(args.COLOR); + const targetColor = Cast.toRgbColorList(args.COLOR2); return util.target.colorIsTouchingColor(targetColor, maskColor); }; Scratch3SensingBlocks.prototype.distanceTo = function (args, util) { if (util.target.isStage) return 10000; - var targetX = 0; - var targetY = 0; + let targetX = 0; + let targetY = 0; if (args.DISTANCETOMENU === '_mouse_') { targetX = util.ioQuery('mouse', 'getX'); targetY = util.ioQuery('mouse', 'getY'); } else { - var distTarget = this.runtime.getSpriteTargetByName( + const distTarget = this.runtime.getSpriteTargetByName( args.DISTANCETOMENU ); if (!distTarget) return 10000; @@ -72,8 +72,8 @@ Scratch3SensingBlocks.prototype.distanceTo = function (args, util) { targetY = distTarget.y; } - var dx = util.target.x - targetX; - var dy = util.target.y - targetY; + const dx = util.target.x - targetX; + const dy = util.target.y - targetY; return Math.sqrt((dx * dx) + (dy * dy)); }; @@ -98,8 +98,8 @@ Scratch3SensingBlocks.prototype.getMouseDown = function (args, util) { }; Scratch3SensingBlocks.prototype.current = function (args) { - var menuOption = Cast.toString(args.CURRENTMENU).toLowerCase(); - var date = new Date(); + const menuOption = Cast.toString(args.CURRENTMENU).toLowerCase(); + const date = new Date(); switch (menuOption) { case 'year': return date.getFullYear(); case 'month': return date.getMonth() + 1; // getMonth is zero-based @@ -117,11 +117,11 @@ Scratch3SensingBlocks.prototype.getKeyPressed = function (args, util) { }; Scratch3SensingBlocks.prototype.daysSince2000 = function () { - var msPerDay = 24 * 60 * 60 * 1000; - var start = new Date(2000, 0, 1); // Months are 0-indexed. - var today = new Date(); - var dstAdjust = today.getTimezoneOffset() - start.getTimezoneOffset(); - var mSecsSinceStart = today.valueOf() - start.valueOf(); + const msPerDay = 24 * 60 * 60 * 1000; + const start = new Date(2000, 0, 1); // Months are 0-indexed. + const today = new Date(); + const dstAdjust = today.getTimezoneOffset() - start.getTimezoneOffset(); + let mSecsSinceStart = today.valueOf() - start.valueOf(); mSecsSinceStart += ((today.getTimezoneOffset() - dstAdjust) * 60 * 1000); return mSecsSinceStart / msPerDay; }; @@ -132,7 +132,7 @@ Scratch3SensingBlocks.prototype.getLoudness = function () { }; Scratch3SensingBlocks.prototype.getAttributeOf = function (args) { - var attrTarget; + let attrTarget; if (args.OBJECT === '_stage_') { attrTarget = this.runtime.getTargetForStage(); @@ -165,7 +165,7 @@ Scratch3SensingBlocks.prototype.getAttributeOf = function (args) { } // Variables - var varName = args.PROPERTY; + const varName = args.PROPERTY; if (attrTarget.variables.hasOwnProperty(varName)) { return attrTarget.variables[varName].value; } diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index c60e41fb..098c1c0f 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -1,8 +1,8 @@ -var MathUtil = require('../util/math-util'); -var Cast = require('../util/cast'); -var Clone = require('../util/clone'); +const MathUtil = require('../util/math-util'); +const Cast = require('../util/cast'); +const Clone = require('../util/clone'); -var Scratch3SoundBlocks = function (runtime) { +const Scratch3SoundBlocks = function (runtime) { /** * The runtime instantiating this block package. * @type {Runtime} @@ -69,7 +69,7 @@ Scratch3SoundBlocks.EFFECT_RANGE = { * @private */ Scratch3SoundBlocks.prototype._getSoundState = function (target) { - var soundState = target.getCustomState(Scratch3SoundBlocks.STATE_KEY); + let soundState = target.getCustomState(Scratch3SoundBlocks.STATE_KEY); if (!soundState) { soundState = Clone.simple(Scratch3SoundBlocks.DEFAULT_SOUND_STATE); target.setCustomState(Scratch3SoundBlocks.STATE_KEY, soundState); @@ -106,18 +106,18 @@ Scratch3SoundBlocks.prototype.getPrimitives = function () { }; Scratch3SoundBlocks.prototype.playSound = function (args, util) { - var index = this._getSoundIndex(args.SOUND_MENU, util); + const index = this._getSoundIndex(args.SOUND_MENU, util); if (index >= 0) { - var md5 = util.target.sprite.sounds[index].md5; + const md5 = util.target.sprite.sounds[index].md5; if (util.target.audioPlayer === null) return; util.target.audioPlayer.playSound(md5); } }; Scratch3SoundBlocks.prototype.playSoundAndWait = function (args, util) { - var index = this._getSoundIndex(args.SOUND_MENU, util); + const index = this._getSoundIndex(args.SOUND_MENU, util); if (index >= 0) { - var md5 = util.target.sprite.sounds[index].md5; + const md5 = util.target.sprite.sounds[index].md5; if (util.target.audioPlayer === null) return; return util.target.audioPlayer.playSound(md5); } @@ -125,15 +125,15 @@ Scratch3SoundBlocks.prototype.playSoundAndWait = function (args, util) { Scratch3SoundBlocks.prototype._getSoundIndex = function (soundName, util) { // if the sprite has no sounds, return -1 - var len = util.target.sprite.sounds.length; + const len = util.target.sprite.sounds.length; if (len === 0) { return -1; } - var index; + let index; // try to convert to a number and use that as an index - var num = parseInt(soundName, 10); + const num = parseInt(soundName, 10); if (!isNaN(num)) { index = MathUtil.wrapClamp(num, 0, len - 1); return index; @@ -145,8 +145,8 @@ Scratch3SoundBlocks.prototype._getSoundIndex = function (soundName, util) { }; Scratch3SoundBlocks.prototype.getSoundIndexByName = function (soundName, util) { - var sounds = util.target.sprite.sounds; - for (var i = 0; i < sounds.length; i++) { + const sounds = util.target.sprite.sounds; + for (let i = 0; i < sounds.length; i++) { if (sounds[i].name === soundName) { return i; } @@ -161,30 +161,30 @@ Scratch3SoundBlocks.prototype.stopAllSounds = function (args, util) { }; Scratch3SoundBlocks.prototype.playNoteForBeats = function (args, util) { - var note = Cast.toNumber(args.NOTE); + let note = Cast.toNumber(args.NOTE); note = MathUtil.clamp(note, Scratch3SoundBlocks.MIDI_NOTE_RANGE.min, Scratch3SoundBlocks.MIDI_NOTE_RANGE.max); - var beats = Cast.toNumber(args.BEATS); + let beats = Cast.toNumber(args.BEATS); beats = this._clampBeats(beats); - var soundState = this._getSoundState(util.target); - var inst = soundState.currentInstrument; - var vol = soundState.volume; + const soundState = this._getSoundState(util.target); + const inst = soundState.currentInstrument; + const vol = soundState.volume; if (typeof this.runtime.audioEngine === 'undefined') return; return this.runtime.audioEngine.playNoteForBeatsWithInstAndVol(note, beats, inst, vol); }; Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) { - var drum = Cast.toNumber(args.DRUM); + let drum = Cast.toNumber(args.DRUM); drum -= 1; // drums are one-indexed if (typeof this.runtime.audioEngine === 'undefined') return; drum = MathUtil.wrapClamp(drum, 0, this.runtime.audioEngine.numDrums); - var beats = Cast.toNumber(args.BEATS); + let beats = Cast.toNumber(args.BEATS); beats = this._clampBeats(beats); if (util.target.audioPlayer === null) return; return util.target.audioPlayer.playDrumForBeats(drum, beats); }; Scratch3SoundBlocks.prototype.restForBeats = function (args) { - var beats = Cast.toNumber(args.BEATS); + let beats = Cast.toNumber(args.BEATS); beats = this._clampBeats(beats); if (typeof this.runtime.audioEngine === 'undefined') return; return this.runtime.audioEngine.waitForBeats(beats); @@ -195,8 +195,8 @@ Scratch3SoundBlocks.prototype._clampBeats = function (beats) { }; Scratch3SoundBlocks.prototype.setInstrument = function (args, util) { - var soundState = this._getSoundState(util.target); - var instNum = Cast.toNumber(args.INSTRUMENT); + const soundState = this._getSoundState(util.target); + let instNum = Cast.toNumber(args.INSTRUMENT); instNum -= 1; // instruments are one-indexed if (typeof this.runtime.audioEngine === 'undefined') return; instNum = MathUtil.wrapClamp(instNum, 0, this.runtime.audioEngine.numInstruments); @@ -213,10 +213,10 @@ Scratch3SoundBlocks.prototype.changeEffect = function (args, util) { }; Scratch3SoundBlocks.prototype._updateEffect = function (args, util, change) { - var effect = Cast.toString(args.EFFECT).toLowerCase(); - var value = Cast.toNumber(args.VALUE); + const effect = Cast.toString(args.EFFECT).toLowerCase(); + const value = Cast.toNumber(args.VALUE); - var soundState = this._getSoundState(util.target); + const soundState = this._getSoundState(util.target); if (!soundState.effects.hasOwnProperty(effect)) return; if (change) { @@ -225,7 +225,7 @@ Scratch3SoundBlocks.prototype._updateEffect = function (args, util, change) { soundState.effects[effect] = value; } - var effectRange = Scratch3SoundBlocks.EFFECT_RANGE[effect]; + const effectRange = Scratch3SoundBlocks.EFFECT_RANGE[effect]; soundState.effects[effect] = MathUtil.clamp(soundState.effects[effect], effectRange.min, effectRange.max); if (util.target.audioPlayer === null) return; @@ -233,8 +233,8 @@ Scratch3SoundBlocks.prototype._updateEffect = function (args, util, change) { }; Scratch3SoundBlocks.prototype.clearEffects = function (args, util) { - var soundState = this._getSoundState(util.target); - for (var effect in soundState.effects) { + const soundState = this._getSoundState(util.target); + for (const effect in soundState.effects) { if (!soundState.effects.hasOwnProperty(effect)) continue; soundState.effects[effect] = 0; } @@ -243,18 +243,18 @@ Scratch3SoundBlocks.prototype.clearEffects = function (args, util) { }; Scratch3SoundBlocks.prototype.setVolume = function (args, util) { - var volume = Cast.toNumber(args.VOLUME); + const volume = Cast.toNumber(args.VOLUME); this._updateVolume(volume, util); }; Scratch3SoundBlocks.prototype.changeVolume = function (args, util) { - var soundState = this._getSoundState(util.target); - var volume = Cast.toNumber(args.VOLUME) + soundState.volume; + const soundState = this._getSoundState(util.target); + const volume = Cast.toNumber(args.VOLUME) + soundState.volume; this._updateVolume(volume, util); }; Scratch3SoundBlocks.prototype._updateVolume = function (volume, util) { - var soundState = this._getSoundState(util.target); + const soundState = this._getSoundState(util.target); volume = MathUtil.clamp(volume, 0, 100); soundState.volume = volume; if (util.target.audioPlayer === null) return; @@ -262,19 +262,19 @@ Scratch3SoundBlocks.prototype._updateVolume = function (volume, util) { }; Scratch3SoundBlocks.prototype.getVolume = function (args, util) { - var soundState = this._getSoundState(util.target); + const soundState = this._getSoundState(util.target); return soundState.volume; }; Scratch3SoundBlocks.prototype.setTempo = function (args) { - var tempo = Cast.toNumber(args.TEMPO); + const tempo = Cast.toNumber(args.TEMPO); this._updateTempo(tempo); }; Scratch3SoundBlocks.prototype.changeTempo = function (args) { - var change = Cast.toNumber(args.TEMPO); + const change = Cast.toNumber(args.TEMPO); if (typeof this.runtime.audioEngine === 'undefined') return; - var tempo = change + this.runtime.audioEngine.currentTempo; + const tempo = change + this.runtime.audioEngine.currentTempo; this._updateTempo(tempo); }; diff --git a/src/engine/adapter.js b/src/engine/adapter.js index 88c8a18c..68a098c6 100644 --- a/src/engine/adapter.js +++ b/src/engine/adapter.js @@ -1,5 +1,5 @@ -var mutationAdapter = require('./mutation-adapter'); -var html = require('htmlparser2'); +const mutationAdapter = require('./mutation-adapter'); +const html = require('htmlparser2'); /** * Convert outer blocks DOM from a Blockly CREATE event @@ -8,22 +8,22 @@ var html = require('htmlparser2'); * @param {Element} blocksDOM DOM tree for this event. * @return {Array.} Usable list of blocks from this CREATE event. */ -var domToBlocks = function (blocksDOM) { +const domToBlocks = function (blocksDOM) { // At this level, there could be multiple blocks adjacent in the DOM tree. - var blocks = {}; - for (var i = 0; i < blocksDOM.length; i++) { - var block = blocksDOM[i]; + const blocks = {}; + for (let i = 0; i < blocksDOM.length; i++) { + const block = blocksDOM[i]; if (!block.name || !block.attribs) { continue; } - var tagName = block.name.toLowerCase(); + const tagName = block.name.toLowerCase(); if (tagName === 'block' || tagName === 'shadow') { domToBlock(block, blocks, true, null); } } // Flatten blocks object into a list. - var blocksList = []; - for (var b in blocks) { + const blocksList = []; + for (const b in blocks) { if (!blocks.hasOwnProperty(b)) continue; blocksList.push(blocks[b]); } @@ -36,7 +36,7 @@ var domToBlocks = function (blocksDOM) { * @param {object} e `Blockly.events.create` * @return {Array.} List of blocks from this CREATE event. */ -var adapter = function (e) { +const adapter = function (e) { // Validate input if (typeof e !== 'object') return; if (typeof e.xml !== 'object') return; @@ -55,7 +55,7 @@ var adapter = function (e) { */ var domToBlock = function (blockDOM, blocks, isTopBlock, parent) { // Block skeleton. - var block = { + const block = { id: blockDOM.attribs.id, // Block ID opcode: blockDOM.attribs.type, // For execution, "event_whengreenflag". inputs: {}, // Inputs to this block and the blocks they point to. @@ -72,18 +72,18 @@ var domToBlock = function (blockDOM, blocks, isTopBlock, parent) { blocks[block.id] = block; // Process XML children and find enclosed blocks, fields, etc. - for (var i = 0; i < blockDOM.children.length; i++) { - var xmlChild = blockDOM.children[i]; + for (let i = 0; i < blockDOM.children.length; i++) { + const xmlChild = blockDOM.children[i]; // Enclosed blocks and shadows - var childBlockNode = null; - var childShadowNode = null; - for (var j = 0; j < xmlChild.children.length; j++) { - var grandChildNode = xmlChild.children[j]; + let childBlockNode = null; + let childShadowNode = null; + for (let j = 0; j < xmlChild.children.length; j++) { + const grandChildNode = xmlChild.children[j]; if (!grandChildNode.name) { // Non-XML tag node. continue; } - var grandChildNodeName = grandChildNode.name.toLowerCase(); + const grandChildNodeName = grandChildNode.name.toLowerCase(); if (grandChildNodeName === 'block') { childBlockNode = grandChildNode; } else if (grandChildNodeName === 'shadow') { diff --git a/src/engine/blocks.js b/src/engine/blocks.js index e1852119..1b562f0d 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -1,6 +1,6 @@ -var adapter = require('./adapter'); -var mutationAdapter = require('./mutation-adapter'); -var xmlEscape = require('../util/xml-escape'); +const adapter = require('./adapter'); +const mutationAdapter = require('./mutation-adapter'); +const xmlEscape = require('../util/xml-escape'); /** * @fileoverview @@ -8,7 +8,7 @@ var xmlEscape = require('../util/xml-escape'); * and handle updates from Scratch Blocks events. */ -var Blocks = function () { +const Blocks = function () { /** * All blocks in the workspace. * Keys are block IDs, values are metadata about the block. @@ -54,7 +54,7 @@ Blocks.prototype.getScripts = function () { * @return {?string} ID of next block in the sequence */ Blocks.prototype.getNextBlock = function (id) { - var block = this._blocks[id]; + const block = this._blocks[id]; return (typeof block === 'undefined') ? null : block.next; }; @@ -65,17 +65,17 @@ Blocks.prototype.getNextBlock = function (id) { * @return {?string} ID of block in the branch. */ Blocks.prototype.getBranch = function (id, branchNum) { - var block = this._blocks[id]; + const block = this._blocks[id]; if (typeof block === 'undefined') return null; if (!branchNum) branchNum = 1; - var inputName = Blocks.BRANCH_INPUT_PREFIX; + let inputName = Blocks.BRANCH_INPUT_PREFIX; if (branchNum > 1) { inputName += branchNum; } // Empty C-block? - var input = block.inputs[inputName]; + const input = block.inputs[inputName]; return (typeof input === 'undefined') ? null : input.block; }; @@ -104,8 +104,8 @@ Blocks.prototype.getFields = function (block) { */ Blocks.prototype.getInputs = function (block) { if (typeof block === 'undefined') return null; - var inputs = {}; - for (var input in block.inputs) { + const inputs = {}; + for (const input in block.inputs) { // Ignore blocks prefixed with branch prefix. if (input.substring(0, Blocks.BRANCH_INPUT_PREFIX.length) !== Blocks.BRANCH_INPUT_PREFIX) { @@ -130,7 +130,7 @@ Blocks.prototype.getMutation = function (block) { * @return {?string} ID of top-level script block. */ Blocks.prototype.getTopLevelScript = function (id) { - var block = this._blocks[id]; + let block = this._blocks[id]; if (typeof block === 'undefined') return null; while (block.parent !== null) { block = this._blocks[block.parent]; @@ -144,9 +144,9 @@ Blocks.prototype.getTopLevelScript = function (id) { * @return {?string} ID of procedure definition. */ Blocks.prototype.getProcedureDefinition = function (name) { - for (var id in this._blocks) { + for (const id in this._blocks) { if (!this._blocks.hasOwnProperty(id)) continue; - var block = this._blocks[id]; + const block = this._blocks[id]; if ((block.opcode === 'procedures_defnoreturn' || block.opcode === 'procedures_defreturn') && block.mutation.proccode === name) { @@ -162,9 +162,9 @@ Blocks.prototype.getProcedureDefinition = function (name) { * @return {?string} ID of procedure definition. */ Blocks.prototype.getProcedureParamNames = function (name) { - for (var id in this._blocks) { + for (const id in this._blocks) { if (!this._blocks.hasOwnProperty(id)) continue; - var block = this._blocks[id]; + const block = this._blocks[id]; if ((block.opcode === 'procedures_defnoreturn' || block.opcode === 'procedures_defreturn') && block.mutation.proccode === name) { @@ -201,7 +201,7 @@ Blocks.prototype.blocklyListen = function (e, optRuntime) { case 'create': var newBlocks = adapter(e); // A create event can create many blocks. Add them all. - for (var i = 0; i < newBlocks.length; i++) { + for (let i = 0; i < newBlocks.length; i++) { this.createBlock(newBlocks[i]); } break; @@ -270,7 +270,7 @@ Blocks.prototype.createBlock = function (block) { Blocks.prototype.changeBlock = function (args) { // Validate if (args.element !== 'field' && args.element !== 'mutation') return; - var block = this._blocks[args.id]; + const block = this._blocks[args.id]; if (typeof block === 'undefined') return; if (args.element === 'field') { @@ -299,7 +299,7 @@ Blocks.prototype.moveBlock = function (e) { // Remove from any old parent. if (typeof e.oldParent !== 'undefined') { - var oldParent = this._blocks[e.oldParent]; + const oldParent = this._blocks[e.oldParent]; if (typeof e.oldInput !== 'undefined' && oldParent.inputs[e.oldInput].block === e.id) { // This block was connected to the old parent's input. @@ -324,7 +324,7 @@ Blocks.prototype.moveBlock = function (e) { } else { // Moved to the new parent's input. // Don't obscure the shadow block. - var oldShadow = null; + let oldShadow = null; if (this._blocks[e.newParent].inputs.hasOwnProperty(e.newInput)) { oldShadow = this._blocks[e.newParent].inputs[e.newInput].shadow; } @@ -346,7 +346,7 @@ Blocks.prototype.deleteBlock = function (e) { // @todo In runtime, stop threads running on this script. // Get block - var block = this._blocks[e.id]; + const block = this._blocks[e.id]; // Delete children if (block.next !== null) { @@ -354,7 +354,7 @@ Blocks.prototype.deleteBlock = function (e) { } // Delete inputs (including branches) - for (var input in block.inputs) { + for (const input in block.inputs) { // If it's null, the block in this input moved away. if (block.inputs[input].block !== null) { this.deleteBlock({id: block.inputs[input].block}); @@ -381,11 +381,11 @@ Blocks.prototype.deleteBlock = function (e) { * @return {string} String of XML representing this object's blocks. */ Blocks.prototype.toXML = function () { - var xmlString = ''; - for (var i = 0; i < this._scripts.length; i++) { + let xmlString = ''; + for (let i = 0; i < this._scripts.length; i++) { xmlString += this.blockToXML(this._scripts[i]); } - return xmlString + ''; + return `${xmlString}`; }; /** @@ -395,29 +395,29 @@ Blocks.prototype.toXML = function () { * @return {string} String of XML representing this block and any children. */ Blocks.prototype.blockToXML = function (blockId) { - var block = this._blocks[blockId]; + const block = this._blocks[blockId]; // Encode properties of this block. - var tagName = (block.shadow) ? 'shadow' : 'block'; - var xy = (block.topLevel) ? - ' x="' + block.x + '" y="' + block.y + '"' : + const tagName = (block.shadow) ? 'shadow' : 'block'; + const xy = (block.topLevel) ? + ` x="${block.x}" y="${block.y}"` : ''; - var xmlString = ''; - xmlString += '<' + tagName + - ' id="' + block.id + '"' + - ' type="' + block.opcode + '"' + - xy + - '>'; + let xmlString = ''; + xmlString += `<${tagName + } id="${block.id}"` + + ` type="${block.opcode}"${ + xy + }>`; // Add any mutation. Must come before inputs. if (block.mutation) { xmlString += this.mutationToXML(block.mutation); } // Add any inputs on this block. - for (var input in block.inputs) { + for (const input in block.inputs) { if (!block.inputs.hasOwnProperty(input)) continue; - var blockInput = block.inputs[input]; + const blockInput = block.inputs[input]; // Only encode a value tag if the value input is occupied. if (blockInput.block || blockInput.shadow) { - xmlString += ''; + xmlString += ``; if (blockInput.block) { xmlString += this.blockToXML(blockInput.block); } @@ -429,21 +429,21 @@ Blocks.prototype.blockToXML = function (blockId) { } } // Add any fields on this block. - for (var field in block.fields) { + for (const field in block.fields) { if (!block.fields.hasOwnProperty(field)) continue; - var blockField = block.fields[field]; - var value = blockField.value; + const blockField = block.fields[field]; + let value = blockField.value; if (typeof value === 'string') { value = xmlEscape(blockField.value); } - xmlString += '' + - value + ''; + xmlString += `${ + value}`; } // Add blocks connected to the next connection. if (block.next) { - xmlString += '' + this.blockToXML(block.next) + ''; + xmlString += `${this.blockToXML(block.next)}`; } - xmlString += ''; + xmlString += ``; return xmlString; }; @@ -453,18 +453,18 @@ Blocks.prototype.blockToXML = function (blockId) { * @return {string} XML string representing a mutation. */ Blocks.prototype.mutationToXML = function (mutation) { - var mutationString = '<' + mutation.tagName; - for (var prop in mutation) { + let mutationString = `<${mutation.tagName}`; + for (const prop in mutation) { if (prop === 'children' || prop === 'tagName') continue; - var mutationValue = (typeof mutation[prop] === 'string') ? + const mutationValue = (typeof mutation[prop] === 'string') ? xmlEscape(mutation[prop]) : mutation[prop]; - mutationString += ' ' + prop + '="' + mutationValue + '"'; + mutationString += ` ${prop}="${mutationValue}"`; } mutationString += '>'; - for (var i = 0; i < mutation.children.length; i++) { + for (let i = 0; i < mutation.children.length; i++) { mutationString += this.mutationToXML(mutation.children[i]); } - mutationString += ''; + mutationString += ``; return mutationString; }; @@ -475,7 +475,7 @@ Blocks.prototype.mutationToXML = function (mutation) { * @param {?string} topBlockId ID of block that starts the script. */ Blocks.prototype._addScript = function (topBlockId) { - var i = this._scripts.indexOf(topBlockId); + const i = this._scripts.indexOf(topBlockId); if (i > -1) return; // Already in scripts. this._scripts.push(topBlockId); // Update `topLevel` property on the top block. @@ -487,7 +487,7 @@ Blocks.prototype._addScript = function (topBlockId) { * @param {?string} topBlockId ID of block that starts the script. */ Blocks.prototype._deleteScript = function (topBlockId) { - var i = this._scripts.indexOf(topBlockId); + const i = this._scripts.indexOf(topBlockId); if (i > -1) this._scripts.splice(i, 1); // Update `topLevel` property on the top block. if (this._blocks[topBlockId]) this._blocks[topBlockId].topLevel = false; diff --git a/src/engine/execute.js b/src/engine/execute.js index 0847c7b4..660eacb3 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -1,12 +1,12 @@ -var log = require('../util/log'); -var Thread = require('./thread'); +const log = require('../util/log'); +const Thread = require('./thread'); /** * Utility function to determine if a value is a Promise. * @param {*} value Value to check for a Promise. * @return {boolean} True if the value appears to be a Promise. */ -var isPromise = function (value) { +const isPromise = function (value) { return value && value.then && typeof value.then === 'function'; }; @@ -16,8 +16,8 @@ var isPromise = function (value) { * @param {!Thread} thread Thread which to read and execute. */ var execute = function (sequencer, thread) { - var runtime = sequencer.runtime; - var target = thread.target; + const runtime = sequencer.runtime; + const target = thread.target; // Stop if block or target no longer exists. if (target === null) { @@ -27,11 +27,11 @@ var execute = function (sequencer, thread) { } // Current block to execute is the one on the top of the stack. - var currentBlockId = thread.peekStack(); - var currentStackFrame = thread.peekStackFrame(); + const currentBlockId = thread.peekStack(); + const currentStackFrame = thread.peekStackFrame(); - var blockContainer = target.blocks; - var block = blockContainer.getBlock(currentBlockId); + let blockContainer = target.blocks; + let block = blockContainer.getBlock(currentBlockId); if (typeof block === 'undefined') { blockContainer = runtime.flyoutBlocks; block = blockContainer.getBlock(currentBlockId); @@ -43,15 +43,15 @@ var execute = function (sequencer, thread) { } } - var opcode = blockContainer.getOpcode(block); - var fields = blockContainer.getFields(block); - var inputs = blockContainer.getInputs(block); - var blockFunction = runtime.getOpcodeFunction(opcode); - var isHat = runtime.getIsHat(opcode); + const opcode = blockContainer.getOpcode(block); + const fields = blockContainer.getFields(block); + const inputs = blockContainer.getInputs(block); + const blockFunction = runtime.getOpcodeFunction(opcode); + const isHat = runtime.getIsHat(opcode); if (!opcode) { - log.warn('Could not get opcode for block: ' + currentBlockId); + log.warn(`Could not get opcode for block: ${currentBlockId}`); return; } @@ -60,18 +60,18 @@ var execute = function (sequencer, thread) { * or after a promise resolves. * @param {*} resolvedValue Value eventually returned from the primitive. */ - var handleReport = function (resolvedValue) { + const handleReport = function (resolvedValue) { thread.pushReportedValue(resolvedValue); if (isHat) { // Hat predicate was evaluated. if (runtime.getIsEdgeActivatedHat(opcode)) { // If this is an edge-activated hat, only proceed if // the value is true and used to be false. - var oldEdgeValue = runtime.updateEdgeActivatedValue( + const oldEdgeValue = runtime.updateEdgeActivatedValue( currentBlockId, resolvedValue ); - var edgeWasActivated = !oldEdgeValue && resolvedValue; + const edgeWasActivated = !oldEdgeValue && resolvedValue; if (!edgeWasActivated) { sequencer.retireThread(thread); } @@ -104,31 +104,31 @@ var execute = function (sequencer, thread) { // Skip through the block (hat with no predicate). return; } - var keys = Object.keys(fields); + const keys = Object.keys(fields); if (keys.length === 1 && Object.keys(inputs).length === 0) { // One field and no inputs - treat as arg. handleReport(fields[keys[0]].value); } else { - log.warn('Could not get implementation for opcode: ' + opcode); + log.warn(`Could not get implementation for opcode: ${opcode}`); } thread.requestScriptGlowInFrame = true; return; } // Generate values for arguments (inputs). - var argValues = {}; + const argValues = {}; // Add all fields on this block to the argValues. - for (var fieldName in fields) { + for (const fieldName in fields) { if (!fields.hasOwnProperty(fieldName)) continue; argValues[fieldName] = fields[fieldName].value; } // Recursively evaluate input blocks. - for (var inputName in inputs) { + for (const inputName in inputs) { if (!inputs.hasOwnProperty(inputName)) continue; - var input = inputs[inputName]; - var inputBlockId = input.block; + const input = inputs[inputName]; + const inputBlockId = input.block; // Is there no value for this input waiting in the stack frame? if (inputBlockId !== null && typeof currentStackFrame.reported[inputName] === 'undefined') { // If there's not, we need to evaluate the block. @@ -151,7 +151,7 @@ var execute = function (sequencer, thread) { } // Add any mutation to args (e.g., for procedures). - var mutation = blockContainer.getMutation(block); + const mutation = blockContainer.getMutation(block); if (mutation !== null) { argValues.mutation = mutation; } @@ -162,7 +162,7 @@ var execute = function (sequencer, thread) { // (e.g., on return from a branch) gets fresh inputs. currentStackFrame.reported = {}; - var primitiveReportedValue = null; + let primitiveReportedValue = null; primitiveReportedValue = blockFunction(argValues, { stackFrame: currentStackFrame.executionContext, target: target, @@ -201,7 +201,7 @@ var execute = function (sequencer, thread) { ioQuery: function (device, func, args) { // Find the I/O device and execute the query/function call. if (runtime.ioDevices[device] && runtime.ioDevices[device][func]) { - var devObject = runtime.ioDevices[device]; + const devObject = runtime.ioDevices[device]; // @todo Figure out why eslint complains about no-useless-call // no-useless-call can't tell if the call is useless for dynamic // expressions... or something. Not exactly sure why it @@ -225,13 +225,13 @@ var execute = function (sequencer, thread) { thread.status = Thread.STATUS_PROMISE_WAIT; } // Promise handlers - primitiveReportedValue.then(function (resolvedValue) { + primitiveReportedValue.then(resolvedValue => { handleReport(resolvedValue); if (typeof resolvedValue === 'undefined') { do { // In the case that the promise is the last block in the current thread stack // We need to pop out repeatedly until we find the next block. - var popped = thread.popStack(); + const popped = thread.popStack(); if (popped === null) { return; } @@ -249,7 +249,7 @@ var execute = function (sequencer, thread) { } else { thread.popStack(); } - }, function (rejectionReason) { + }, rejectionReason => { // Promise rejected: the primitive had some error. // Log it and proceed. log.warn('Primitive rejected promise: ', rejectionReason); diff --git a/src/engine/list.js b/src/engine/list.js index d8be65a3..c26213e2 100644 --- a/src/engine/list.js +++ b/src/engine/list.js @@ -8,7 +8,7 @@ * @param {Array} contents Contents of the list, as an array. * @constructor */ -var List = function (name, contents) { +const List = function (name, contents) { this.name = name; this.contents = contents; }; diff --git a/src/engine/mutation-adapter.js b/src/engine/mutation-adapter.js index 54bf7b4e..a057f80f 100644 --- a/src/engine/mutation-adapter.js +++ b/src/engine/mutation-adapter.js @@ -1,4 +1,4 @@ -var html = require('htmlparser2'); +const html = require('htmlparser2'); /** * Convert a part of a mutation DOM to a mutation VM object, recursively. @@ -6,14 +6,14 @@ var html = require('htmlparser2'); * @return {object} Object representing useful parts of this mutation. */ var mutatorTagToObject = function (dom) { - var obj = Object.create(null); + const obj = Object.create(null); obj.tagName = dom.name; obj.children = []; - for (var prop in dom.attribs) { + for (const prop in dom.attribs) { if (prop === 'xmlns') continue; obj[prop] = dom.attribs[prop]; } - for (var i = 0; i < dom.children.length; i++) { + for (let i = 0; i < dom.children.length; i++) { obj.children.push( mutatorTagToObject(dom.children[i]) ); @@ -27,8 +27,8 @@ var mutatorTagToObject = function (dom) { * @param {(object|string)} mutation Mutation XML string or DOM. * @return {object} Object representing the mutation. */ -var mutationAdpater = function (mutation) { - var mutationParsed; +const mutationAdpater = function (mutation) { + let mutationParsed; // Check if the mutation is already parsed; if not, parse it. if (typeof mutation === 'object') { mutationParsed = mutation; diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 65f3c5fc..6bde2ddf 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -1,15 +1,15 @@ -var EventEmitter = require('events'); -var Sequencer = require('./sequencer'); -var Blocks = require('./blocks'); -var Thread = require('./thread'); -var util = require('util'); +const EventEmitter = require('events'); +const Sequencer = require('./sequencer'); +const Blocks = require('./blocks'); +const Thread = require('./thread'); +const util = require('util'); // Virtual I/O devices. -var Clock = require('../io/clock'); -var Keyboard = require('../io/keyboard'); -var Mouse = require('../io/mouse'); +const Clock = require('../io/clock'); +const Keyboard = require('../io/keyboard'); +const Mouse = require('../io/mouse'); -var defaultBlockPackages = { +const defaultBlockPackages = { scratch3_control: require('../blocks/scratch3_control'), scratch3_event: require('../blocks/scratch3_event'), scratch3_looks: require('../blocks/scratch3_looks'), @@ -26,7 +26,7 @@ var defaultBlockPackages = { * Manages targets, scripts, and the sequencer. * @constructor */ -var Runtime = function () { +const Runtime = function () { // Bind event emitter EventEmitter.call(this); @@ -237,14 +237,14 @@ Runtime.MAX_CLONES = 300; * @private */ Runtime.prototype._registerBlockPackages = function () { - for (var packageName in defaultBlockPackages) { + for (const packageName in defaultBlockPackages) { if (defaultBlockPackages.hasOwnProperty(packageName)) { // @todo pass a different runtime depending on package privilege? - var packageObject = new (defaultBlockPackages[packageName])(this); + const packageObject = new (defaultBlockPackages[packageName])(this); // Collect primitives from package. if (packageObject.getPrimitives) { - var packagePrimitives = packageObject.getPrimitives(); - for (var op in packagePrimitives) { + const packagePrimitives = packageObject.getPrimitives(); + for (const op in packagePrimitives) { if (packagePrimitives.hasOwnProperty(op)) { this._primitives[op] = packagePrimitives[op].bind(packageObject); @@ -253,8 +253,8 @@ Runtime.prototype._registerBlockPackages = function () { } // Collect hat metadata from package. if (packageObject.getHats) { - var packageHats = packageObject.getHats(); - for (var hatName in packageHats) { + const packageHats = packageObject.getHats(); + for (const hatName in packageHats) { if (packageHats.hasOwnProperty(hatName)) { this._hats[hatName] = packageHats[hatName]; } @@ -299,7 +299,7 @@ Runtime.prototype.getIsEdgeActivatedHat = function (opcode) { * @return {*} The old value for the edge-activated hat. */ Runtime.prototype.updateEdgeActivatedValue = function (blockId, newValue) { - var oldValue = this._edgeActivatedHatValues[blockId]; + const oldValue = this._edgeActivatedHatValues[blockId]; this._edgeActivatedHatValues[blockId] = newValue; return oldValue; }; @@ -345,7 +345,7 @@ Runtime.prototype.attachStorage = function (storage) { * @return {!Thread} The newly created thread. */ Runtime.prototype._pushThread = function (id, target) { - var thread = new Thread(id); + const thread = new Thread(id); thread.target = target; thread.pushStack(id); this.threads.push(thread); @@ -360,7 +360,7 @@ 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); + const i = this.threads.indexOf(thread); if (i > -1) { this.threads.splice(i, 1); } @@ -373,10 +373,10 @@ Runtime.prototype._removeThread = function (thread) { * @param {!Thread} thread Thread object to restart. */ Runtime.prototype._restartThread = function (thread) { - var newThread = new Thread(thread.topBlock); + const newThread = new Thread(thread.topBlock); newThread.target = thread.target; newThread.pushStack(thread.topBlock); - var i = this.threads.indexOf(thread); + const i = this.threads.indexOf(thread); if (i > -1) { this.threads[i] = newThread; } else { @@ -399,7 +399,7 @@ Runtime.prototype.isActiveThread = function (thread) { */ Runtime.prototype.toggleScript = function (topBlockId) { // Remove any existing thread. - for (var i = 0; i < this.threads.length; i++) { + for (let i = 0; i < this.threads.length; i++) { if (this.threads[i].topBlock === topBlockId) { this._removeThread(this.threads[i]); return; @@ -418,15 +418,15 @@ Runtime.prototype.toggleScript = function (topBlockId) { * @param {Target=} optTarget Optionally, a target to restrict to. */ Runtime.prototype.allScriptsDo = function (f, optTarget) { - var targets = this.targets; + let targets = this.targets; if (optTarget) { targets = [optTarget]; } - for (var t = targets.length - 1; t >= 0; t--) { - var target = targets[t]; - var scripts = target.blocks.getScripts(); - for (var j = 0; j < scripts.length; j++) { - var topBlockId = scripts[j]; + for (let t = targets.length - 1; t >= 0; t--) { + const target = targets[t]; + const scripts = target.blocks.getScripts(); + for (let j = 0; j < scripts.length; j++) { + const topBlockId = scripts[j]; f(topBlockId, target); } } @@ -445,19 +445,19 @@ Runtime.prototype.startHats = function (requestedHatOpcode, // No known hat with this opcode. return; } - var instance = this; - var newThreads = []; + const instance = this; + const newThreads = []; - for (var opts in optMatchFields) { + for (const opts in optMatchFields) { if (!optMatchFields.hasOwnProperty(opts)) continue; optMatchFields[opts] = optMatchFields[opts].toUpperCase(); } // Consider all scripts, looking for hats with opcode `requestedHatOpcode`. - this.allScriptsDo(function (topBlockId, target) { - var blocks = target.blocks; - var block = blocks.getBlock(topBlockId); - var potentialHatOpcode = block.opcode; + this.allScriptsDo((topBlockId, target) => { + const blocks = target.blocks; + const block = blocks.getBlock(topBlockId); + const potentialHatOpcode = block.opcode; if (potentialHatOpcode !== requestedHatOpcode) { // Not the right hat. return; @@ -468,22 +468,22 @@ Runtime.prototype.startHats = function (requestedHatOpcode, // This needs to happen before the block is evaluated // (i.e., before the predicate can be run) because "broadcast and wait" // needs to have a precise collection of started threads. - var hatFields = blocks.getFields(block); + let hatFields = blocks.getFields(block); // If no fields are present, check inputs (horizontal blocks) if (Object.keys(hatFields).length === 0) { - var hatInputs = blocks.getInputs(block); - for (var input in hatInputs) { + const hatInputs = blocks.getInputs(block); + for (const input in hatInputs) { if (!hatInputs.hasOwnProperty(input)) continue; - var id = hatInputs[input].block; - var inpBlock = blocks.getBlock(id); - var fields = blocks.getFields(inpBlock); + const id = hatInputs[input].block; + const inpBlock = blocks.getBlock(id); + const fields = blocks.getFields(inpBlock); hatFields = Object.assign(fields, hatFields); } } if (optMatchFields) { - for (var matchField in optMatchFields) { + for (const matchField in optMatchFields) { if (hatFields[matchField].value.toUpperCase() !== optMatchFields[matchField]) { // Field mismatch. @@ -493,11 +493,11 @@ Runtime.prototype.startHats = function (requestedHatOpcode, } // Look up metadata for the relevant hat. - var hatMeta = instance._hats[requestedHatOpcode]; + const hatMeta = instance._hats[requestedHatOpcode]; if (hatMeta.restartExistingThreads) { // If `restartExistingThreads` is true, we should stop // any existing threads starting with the top block. - for (var i = 0; i < instance.threads.length; i++) { + for (let i = 0; i < instance.threads.length; i++) { if (instance.threads[i].topBlock === topBlockId && instance.threads[i].target === target) { instance._restartThread(instance.threads[i]); @@ -507,7 +507,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode, } else { // If `restartExistingThreads` is false, we should // give up if any threads with the top block are running. - for (var j = 0; j < instance.threads.length; j++) { + for (let j = 0; j < instance.threads.length; j++) { if (instance.threads[j].topBlock === topBlockId && instance.threads[j].target === target) { // Some thread is already running. @@ -534,7 +534,7 @@ Runtime.prototype.dispose = function () { * @param {!Target} disposingTarget Target to dispose of. */ Runtime.prototype.disposeTarget = function (disposingTarget) { - this.targets = this.targets.filter(function (target) { + this.targets = this.targets.filter(target => { if (disposingTarget !== target) return true; // Allow target to do dispose actions. target.dispose(); @@ -550,7 +550,7 @@ Runtime.prototype.disposeTarget = function (disposingTarget) { */ Runtime.prototype.stopForTarget = function (target, optThreadException) { // Stop any threads on the target. - for (var i = 0; i < this.threads.length; i++) { + for (let i = 0; i < this.threads.length; i++) { if (this.threads[i] === optThreadException) { continue; } @@ -568,7 +568,7 @@ Runtime.prototype.greenFlag = function () { this.ioDevices.clock.resetProjectTimer(); this.clearEdgeActivatedValues(); // Inform all targets of the green flag. - for (var i = 0; i < this.targets.length; i++) { + for (let i = 0; i < this.targets.length; i++) { this.targets[i].onGreenFlag(); } this.startHats('event_whenflagclicked'); @@ -579,8 +579,8 @@ Runtime.prototype.greenFlag = function () { */ Runtime.prototype.stopAll = function () { // Dispose all clones. - var newTargets = []; - for (var i = 0; i < this.targets.length; i++) { + const newTargets = []; + for (let i = 0; i < this.targets.length; i++) { this.targets[i].onStopAll(); if (this.targets[i].hasOwnProperty('isOriginal') && !this.targets[i].isOriginal) { @@ -591,9 +591,9 @@ Runtime.prototype.stopAll = function () { } this.targets = newTargets; // Dispose all threads. - var threadsCopy = this.threads.slice(); + const threadsCopy = this.threads.slice(); while (threadsCopy.length > 0) { - var poppedThread = threadsCopy.pop(); + const poppedThread = threadsCopy.pop(); this._removeThread(poppedThread); } }; @@ -604,15 +604,15 @@ Runtime.prototype.stopAll = function () { */ Runtime.prototype._step = function () { // Find all edge-activated hats, and add them to threads to be evaluated. - for (var hatType in this._hats) { + for (const hatType in this._hats) { if (!this._hats.hasOwnProperty(hatType)) continue; - var hat = this._hats[hatType]; + const hat = this._hats[hatType]; if (hat.edgeActivated) { this.startHats(hatType); } } this.redrawRequested = false; - var doneThreads = this.sequencer.stepThreads(); + const doneThreads = this.sequencer.stepThreads(); this._updateGlows(doneThreads); this._setThreadCount(this.threads.length + doneThreads.length); if (this.renderer) { @@ -651,23 +651,23 @@ Runtime.prototype.setCompatibilityMode = function (compatibilityModeOn) { * @param {Array.=} optExtraThreads Optional list of inactive threads. */ Runtime.prototype._updateGlows = function (optExtraThreads) { - var searchThreads = []; + const searchThreads = []; searchThreads.push.apply(searchThreads, this.threads); if (optExtraThreads) { searchThreads.push.apply(searchThreads, optExtraThreads); } // Set of scripts that request a glow this frame. - var requestedGlowsThisFrame = []; + const requestedGlowsThisFrame = []; // Final set of scripts glowing during this frame. - var finalScriptGlows = []; + const finalScriptGlows = []; // Find all scripts that should be glowing. - for (var i = 0; i < searchThreads.length; i++) { - var thread = searchThreads[i]; - var target = thread.target; + for (let i = 0; i < searchThreads.length; i++) { + const thread = searchThreads[i]; + const target = thread.target; if (target === this._editingTarget) { - var blockForThread = thread.blockGlowInFrame; + const blockForThread = thread.blockGlowInFrame; if (thread.requestScriptGlowInFrame) { - var script = target.blocks.getTopLevelScript(blockForThread); + let script = target.blocks.getTopLevelScript(blockForThread); if (!script) { // Attempt to find in flyout blocks. script = this.flyoutBlocks.getTopLevelScript( @@ -681,8 +681,8 @@ Runtime.prototype._updateGlows = function (optExtraThreads) { } } // Compare to previous frame. - for (var j = 0; j < this._scriptGlowsPreviousFrame.length; j++) { - var previousFrameGlow = this._scriptGlowsPreviousFrame[j]; + for (let j = 0; j < this._scriptGlowsPreviousFrame.length; j++) { + const previousFrameGlow = this._scriptGlowsPreviousFrame[j]; if (requestedGlowsThisFrame.indexOf(previousFrameGlow) < 0) { // Glow turned off. this.glowScript(previousFrameGlow, false); @@ -691,8 +691,8 @@ Runtime.prototype._updateGlows = function (optExtraThreads) { finalScriptGlows.push(previousFrameGlow); } } - for (var k = 0; k < requestedGlowsThisFrame.length; k++) { - var currentFrameGlow = requestedGlowsThisFrame[k]; + for (let k = 0; k < requestedGlowsThisFrame.length; k++) { + const currentFrameGlow = requestedGlowsThisFrame[k]; if (this._scriptGlowsPreviousFrame.indexOf(currentFrameGlow) < 0) { // Glow turned on. this.glowScript(currentFrameGlow, true); @@ -725,7 +725,7 @@ Runtime.prototype._setThreadCount = function (threadCount) { * @param {!string} scriptBlockId Id of top-level block in script to quiet. */ Runtime.prototype.quietGlow = function (scriptBlockId) { - var index = this._scriptGlowsPreviousFrame.indexOf(scriptBlockId); + const index = this._scriptGlowsPreviousFrame.indexOf(scriptBlockId); if (index > -1) { this._scriptGlowsPreviousFrame.splice(index, 1); } @@ -782,8 +782,8 @@ Runtime.prototype.spriteInfoReport = function (target) { * @return {?Target} The target, if found. */ Runtime.prototype.getTargetById = function (targetId) { - for (var i = 0; i < this.targets.length; i++) { - var target = this.targets[i]; + for (let i = 0; i < this.targets.length; i++) { + const target = this.targets[i]; if (target.id === targetId) { return target; } @@ -796,8 +796,8 @@ Runtime.prototype.getTargetById = function (targetId) { * @return {?Target} Target representing a sprite of the given name. */ Runtime.prototype.getSpriteTargetByName = function (spriteName) { - for (var i = 0; i < this.targets.length; i++) { - var target = this.targets[i]; + for (let i = 0; i < this.targets.length; i++) { + const target = this.targets[i]; if (target.sprite && target.sprite.name === spriteName) { return target; } @@ -810,8 +810,8 @@ Runtime.prototype.getSpriteTargetByName = function (spriteName) { * @return {?Target} The target, if found */ Runtime.prototype.getTargetByDrawableId = function (drawableID) { - for (var i = 0; i < this.targets.length; i++) { - var target = this.targets[i]; + for (let i = 0; i < this.targets.length; i++) { + const target = this.targets[i]; if (target.drawableID === drawableID) return target; } }; @@ -837,8 +837,8 @@ Runtime.prototype.clonesAvailable = function () { * @return {?Target} The target, if found. */ Runtime.prototype.getTargetForStage = function () { - for (var i = 0; i < this.targets.length; i++) { - var target = this.targets[i]; + for (let i = 0; i < this.targets.length; i++) { + const target = this.targets[i]; if (target.isStage) { return target; } @@ -857,14 +857,14 @@ Runtime.prototype.requestRedraw = function () { * Set up timers to repeatedly step in a browser. */ Runtime.prototype.start = function () { - var interval = Runtime.THREAD_STEP_INTERVAL; + let interval = Runtime.THREAD_STEP_INTERVAL; if (this.compatibilityMode) { interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY; } this.currentStepTime = interval; - this._steppingInterval = setInterval(function () { + this._steppingInterval = setInterval(() => { this._step(); - }.bind(this), interval); + }, interval); }; module.exports = Runtime; diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js index 06deb38f..bb6bbe4c 100644 --- a/src/engine/sequencer.js +++ b/src/engine/sequencer.js @@ -1,8 +1,8 @@ -var Timer = require('../util/timer'); -var Thread = require('./thread'); -var execute = require('./execute.js'); +const Timer = require('../util/timer'); +const Thread = require('./thread'); +const execute = require('./execute.js'); -var Sequencer = function (runtime) { +const Sequencer = function (runtime) { /** * A utility timer for timing thread sequencing. * @type {!Timer} @@ -28,14 +28,14 @@ Sequencer.WARP_TIME = 500; */ Sequencer.prototype.stepThreads = function () { // Work time is 75% of the thread stepping interval. - var WORK_TIME = 0.75 * this.runtime.currentStepTime; + const WORK_TIME = 0.75 * this.runtime.currentStepTime; // Start counting toward WORK_TIME. this.timer.start(); // Count of active threads. - var numActiveThreads = Infinity; + let numActiveThreads = Infinity; // Whether `stepThreads` has run through a full single tick. - var ranFirstTick = false; - var doneThreads = []; + let ranFirstTick = false; + const doneThreads = []; // Conditions for continuing to stepping threads: // 1. We must have threads in the list, and some must be active. // 2. Time elapsed must be less than WORK_TIME. @@ -46,8 +46,8 @@ Sequencer.prototype.stepThreads = function () { (this.runtime.turboMode || !this.runtime.redrawRequested)) { numActiveThreads = 0; // Attempt to run each thread one time. - for (var i = 0; i < this.runtime.threads.length; i++) { - var activeThread = this.runtime.threads[i]; + for (let i = 0; i < this.runtime.threads.length; i++) { + const activeThread = this.runtime.threads[i]; if (activeThread.stack.length === 0 || activeThread.status === Thread.STATUS_DONE) { // Finished with this thread. @@ -76,7 +76,7 @@ Sequencer.prototype.stepThreads = function () { ranFirstTick = true; } // Filter inactive threads from `this.runtime.threads`. - this.runtime.threads = this.runtime.threads.filter(function (thread) { + this.runtime.threads = this.runtime.threads.filter(thread => { if (doneThreads.indexOf(thread) > -1) { return false; } @@ -90,13 +90,13 @@ Sequencer.prototype.stepThreads = function () { * @param {!Thread} thread Thread object to step. */ Sequencer.prototype.stepThread = function (thread) { - var currentBlockId = thread.peekStack(); + let currentBlockId = thread.peekStack(); if (!currentBlockId) { // A "null block" - empty branch. thread.popStack(); } while (thread.peekStack()) { - var isWarpMode = thread.peekStackFrame().warpMode; + let isWarpMode = thread.peekStackFrame().warpMode; if (isWarpMode && !thread.warpTimer) { // Initialize warp-mode timer if it hasn't been already. // This will start counting the thread toward `Sequencer.WARP_TIME`. @@ -138,7 +138,7 @@ Sequencer.prototype.stepThread = function (thread) { return; } - var stackFrame = thread.peekStackFrame(); + const stackFrame = thread.peekStackFrame(); isWarpMode = stackFrame.warpMode; if (stackFrame.isLoop) { @@ -151,11 +151,11 @@ Sequencer.prototype.stepThread = function (thread) { // Don't do anything to the stack, since loops need // to be re-executed. return; - } else { + } // Don't go to the next block for this level of the stack, // since loops need to be re-executed. - continue; - } + continue; + } else if (stackFrame.waitingReporter) { // This level of the stack was waiting for a value. // This means a reporter has just returned - so don't go @@ -178,8 +178,8 @@ Sequencer.prototype.stepToBranch = function (thread, branchNum, isLoop) { if (!branchNum) { branchNum = 1; } - var currentBlockId = thread.peekStack(); - var branchId = thread.target.blocks.getBranch( + const currentBlockId = thread.peekStack(); + const branchId = thread.target.blocks.getBranch( currentBlockId, branchNum ); @@ -198,13 +198,13 @@ Sequencer.prototype.stepToBranch = function (thread, branchNum, isLoop) { * @param {!string} procedureCode Procedure code of procedure to step to. */ Sequencer.prototype.stepToProcedure = function (thread, procedureCode) { - var definition = thread.target.blocks.getProcedureDefinition(procedureCode); + const definition = thread.target.blocks.getProcedureDefinition(procedureCode); if (!definition) { return; } // Check if the call is recursive. // If so, set the thread to yield after pushing. - var isRecursive = thread.isRecursiveCall(procedureCode); + const isRecursive = thread.isRecursiveCall(procedureCode); // To step to a procedure, we put its definition on the stack. // Execution for the thread will proceed through the definition hat // and on to the main definition of the procedure. @@ -218,8 +218,8 @@ Sequencer.prototype.stepToProcedure = function (thread, procedureCode) { } else { // Look for warp-mode flag on definition, and set the thread // to warp-mode if needed. - var definitionBlock = thread.target.blocks.getBlock(definition); - var doWarp = definitionBlock.mutation.warp; + const definitionBlock = thread.target.blocks.getBlock(definition); + const doWarp = definitionBlock.mutation.warp; if (doWarp) { thread.peekStackFrame().warpMode = true; } else { diff --git a/src/engine/target.js b/src/engine/target.js index 947df53d..0c5c9881 100644 --- a/src/engine/target.js +++ b/src/engine/target.js @@ -1,10 +1,10 @@ -var EventEmitter = require('events'); -var util = require('util'); +const EventEmitter = require('events'); +const util = require('util'); -var Blocks = require('./blocks'); -var Variable = require('../engine/variable'); -var List = require('../engine/list'); -var uid = require('../util/uid'); +const Blocks = require('./blocks'); +const Variable = require('../engine/variable'); +const List = require('../engine/list'); +const uid = require('../util/uid'); /** * @fileoverview @@ -16,7 +16,7 @@ var uid = require('../util/uid'); * @param {?Blocks} blocks Blocks instance for the blocks owned by this target. * @constructor */ -var Target = function (blocks) { +const Target = function (blocks) { EventEmitter.call(this); if (!blocks) { @@ -87,13 +87,13 @@ Target.prototype.lookupOrCreateVariable = function (name) { } // If the stage has a global copy, return it. if (this.runtime && !this.isStage) { - var stage = this.runtime.getTargetForStage(); + const stage = this.runtime.getTargetForStage(); if (stage.variables.hasOwnProperty(name)) { return stage.variables[name]; } } // No variable with this name exists - create it locally. - var newVariable = new Variable(name, 0, false); + const newVariable = new Variable(name, 0, false); this.variables[name] = newVariable; return newVariable; }; @@ -111,13 +111,13 @@ Target.prototype.lookupOrCreateList = function (name) { } // If the stage has a global copy, return it. if (this.runtime && !this.isStage) { - var stage = this.runtime.getTargetForStage(); + const stage = this.runtime.getTargetForStage(); if (stage.lists.hasOwnProperty(name)) { return stage.lists[name]; } } // No list with this name exists - create it locally. - var newList = new List(name, []); + const newList = new List(name, []); this.lists[name] = newList; return newList; }; diff --git a/src/engine/thread.js b/src/engine/thread.js index f093f886..bedd132d 100644 --- a/src/engine/thread.js +++ b/src/engine/thread.js @@ -3,7 +3,7 @@ * @param {?string} firstBlock First block to execute in the thread. * @constructor */ -var Thread = function (firstBlock) { +const Thread = function (firstBlock) { /** * ID of top block of the thread * @type {!string} @@ -100,7 +100,7 @@ Thread.prototype.pushStack = function (blockId) { // Might not, if we just popped the stack. if (this.stack.length > this.stackFrames.length) { // Copy warp mode from any higher level. - var warpMode = false; + let warpMode = false; if (this.stackFrames.length > 0 && this.stackFrames[this.stackFrames.length - 1]) { warpMode = this.stackFrames[this.stackFrames.length - 1].warpMode; } @@ -122,7 +122,7 @@ Thread.prototype.pushStack = function (blockId) { */ Thread.prototype.reuseStackForNextBlock = function (blockId) { this.stack[this.stack.length - 1] = blockId; - var frame = this.stackFrames[this.stackFrames.length - 1]; + const frame = this.stackFrames[this.stackFrames.length - 1]; frame.isLoop = false; // frame.warpMode = warpMode; // warp mode stays the same when reusing the stack frame. frame.reported = {}; @@ -144,9 +144,9 @@ Thread.prototype.popStack = function () { * Pop back down the stack frame until we hit a procedure call or the stack frame is emptied */ Thread.prototype.stopThisScript = function () { - var blockID = this.peekStack(); + let blockID = this.peekStack(); while (blockID !== null) { - var block = this.target.blocks.getBlock(blockID); + const block = this.target.blocks.getBlock(blockID); if (typeof block !== 'undefined' && block.opcode === 'procedures_callnoreturn') { break; } @@ -191,9 +191,9 @@ Thread.prototype.peekParentStackFrame = function () { * @param {*} value Reported value to push. */ Thread.prototype.pushReportedValue = function (value) { - var parentStackFrame = this.peekParentStackFrame(); + const parentStackFrame = this.peekParentStackFrame(); if (parentStackFrame) { - var waitingReporter = parentStackFrame.waitingReporter; + const waitingReporter = parentStackFrame.waitingReporter; parentStackFrame.reported[waitingReporter] = value; } }; @@ -205,7 +205,7 @@ Thread.prototype.pushReportedValue = function (value) { * @param {*} value Value to set for parameter. */ Thread.prototype.pushParam = function (paramName, value) { - var stackFrame = this.peekStackFrame(); + const stackFrame = this.peekStackFrame(); stackFrame.params[paramName] = value; }; @@ -215,8 +215,8 @@ Thread.prototype.pushParam = function (paramName, value) { * @return {*} value Value for parameter. */ Thread.prototype.getParam = function (paramName) { - for (var i = this.stackFrames.length - 1; i >= 0; i--) { - var frame = this.stackFrames[i]; + for (let i = this.stackFrames.length - 1; i >= 0; i--) { + const frame = this.stackFrames[i]; if (frame.params.hasOwnProperty(paramName)) { return frame.params[paramName]; } @@ -239,7 +239,7 @@ Thread.prototype.atStackTop = function () { * where execution proceeds from one block to the next. */ Thread.prototype.goToNextBlock = function () { - var nextBlockId = this.target.blocks.getNextBlock(this.peekStack()); + const nextBlockId = this.target.blocks.getNextBlock(this.peekStack()); this.reuseStackForNextBlock(nextBlockId); }; @@ -250,10 +250,10 @@ Thread.prototype.goToNextBlock = function () { * @return {boolean} True if the call appears recursive. */ Thread.prototype.isRecursiveCall = function (procedureCode) { - var callCount = 5; // Max number of enclosing procedure calls to examine. - var sp = this.stack.length - 1; - for (var i = sp - 1; i >= 0; i--) { - var block = this.target.blocks.getBlock(this.stack[i]); + let callCount = 5; // Max number of enclosing procedure calls to examine. + const sp = this.stack.length - 1; + for (let i = sp - 1; i >= 0; i--) { + const block = this.target.blocks.getBlock(this.stack[i]); if (block.opcode === 'procedures_callnoreturn' && block.mutation.proccode === procedureCode) { return true; diff --git a/src/engine/variable.js b/src/engine/variable.js index 3f2dcc5d..eab090ca 100644 --- a/src/engine/variable.js +++ b/src/engine/variable.js @@ -9,7 +9,7 @@ * @param {boolean} isCloud Whether the variable is stored in the cloud. * @constructor */ -var Variable = function (name, value, isCloud) { +const Variable = function (name, value, isCloud) { this.name = name; this.value = value; this.isCloud = isCloud; diff --git a/src/import/load-costume.js b/src/import/load-costume.js index bc7e3f17..4c4ed9ac 100644 --- a/src/import/load-costume.js +++ b/src/import/load-costume.js @@ -1,5 +1,5 @@ -var AssetType = require('scratch-storage').AssetType; -var log = require('../util/log'); +const AssetType = require('scratch-storage').AssetType; +const log = require('../util/log'); /** * Load a costume's asset into memory asynchronously. @@ -13,62 +13,58 @@ var log = require('../util/log'); * @param {!Runtime} runtime - Scratch runtime, used to access the storage module. * @returns {?Promise} - a promise which will resolve after skinId is set, or null on error. */ -var loadCostume = function (md5ext, costume, runtime) { +const loadCostume = function (md5ext, costume, runtime) { if (!runtime.storage) { log.error('No storage module present; cannot load costume asset: ', md5ext); return Promise.resolve(costume); } - var idParts = md5ext.split('.'); - var md5 = idParts[0]; - var ext = idParts[1].toUpperCase(); - var assetType = (ext === 'SVG') ? AssetType.ImageVector : AssetType.ImageBitmap; + const idParts = md5ext.split('.'); + const md5 = idParts[0]; + const ext = idParts[1].toUpperCase(); + const assetType = (ext === 'SVG') ? AssetType.ImageVector : AssetType.ImageBitmap; - var rotationCenter = [ + const rotationCenter = [ costume.rotationCenterX / costume.bitmapResolution, costume.rotationCenterY / costume.bitmapResolution ]; - var promise = runtime.storage.load(assetType, md5).then(function (costumeAsset) { + let promise = runtime.storage.load(assetType, md5).then(costumeAsset => { costume.url = costumeAsset.encodeDataURI(); return costumeAsset; }); if (!runtime.renderer) { log.error('No rendering module present; cannot load costume asset: ', md5ext); - return promise.then(function () { - return costume; - }); + return promise.then(() => costume); } if (assetType === AssetType.ImageVector) { - promise = promise.then(function (costumeAsset) { + promise = promise.then(costumeAsset => { costume.skinId = runtime.renderer.createSVGSkin(costumeAsset.decodeText(), rotationCenter); return costume; }); } else { - promise = promise.then(function (costumeAsset) { - return new Promise(function (resolve, reject) { - var imageElement = new Image(); - var removeEventListeners; // fix no-use-before-define - var onError = function () { - removeEventListeners(); - reject(); - }; - var onLoad = function () { - removeEventListeners(); - resolve(imageElement); - }; - removeEventListeners = function () { - imageElement.removeEventListener('error', onError); - imageElement.removeEventListener('load', onLoad); - }; - imageElement.addEventListener('error', onError); - imageElement.addEventListener('load', onLoad); - imageElement.src = costumeAsset.encodeDataURI(); - }); - }).then(function (imageElement) { + promise = promise.then(costumeAsset => new Promise((resolve, reject) => { + const imageElement = new Image(); + let removeEventListeners; // fix no-use-before-define + const onError = function () { + removeEventListeners(); + reject(); + }; + const onLoad = function () { + removeEventListeners(); + resolve(imageElement); + }; + removeEventListeners = function () { + imageElement.removeEventListener('error', onError); + imageElement.removeEventListener('load', onLoad); + }; + imageElement.addEventListener('error', onError); + imageElement.addEventListener('load', onLoad); + imageElement.src = costumeAsset.encodeDataURI(); + })).then(imageElement => { costume.skinId = runtime.renderer.createBitmapSkin(imageElement, costume.bitmapResolution, rotationCenter); return costume; }); diff --git a/src/import/load-sound.js b/src/import/load-sound.js index 74856326..6502b793 100644 --- a/src/import/load-sound.js +++ b/src/import/load-sound.js @@ -1,5 +1,5 @@ -var AssetType = require('scratch-storage').AssetType; -var log = require('../util/log'); +const AssetType = require('scratch-storage').AssetType; +const log = require('../util/log'); /** * Load a sound's asset into memory asynchronously. @@ -9,7 +9,7 @@ var log = require('../util/log'); * @param {!Runtime} runtime - Scratch runtime, used to access the storage module. * @returns {!Promise} - a promise which will resolve after sound is loaded */ -var loadSound = function (sound, runtime) { +const loadSound = function (sound, runtime) { if (!runtime.storage) { log.error('No storage module present; cannot load sound asset: ', sound.md5); return Promise.resolve(sound); @@ -18,9 +18,9 @@ var loadSound = function (sound, runtime) { log.error('No audio engine present; cannot load sound asset: ', sound.md5); return Promise.resolve(sound); } - var idParts = sound.md5.split('.'); - var md5 = idParts[0]; - return runtime.storage.load(AssetType.Sound, md5).then(function (soundAsset) { + const idParts = sound.md5.split('.'); + const md5 = idParts[0]; + return runtime.storage.load(AssetType.Sound, md5).then(soundAsset => { sound.data = soundAsset.data; return runtime.audioEngine.decodeSound(sound); }); diff --git a/src/import/sb2import.js b/src/import/sb2import.js index ac905d33..c896d01d 100644 --- a/src/import/sb2import.js +++ b/src/import/sb2import.js @@ -5,18 +5,18 @@ * scratch-vm runtime structures. */ -var Blocks = require('../engine/blocks'); -var RenderedTarget = require('../sprites/rendered-target'); -var Sprite = require('../sprites/sprite'); -var Color = require('../util/color'); -var log = require('../util/log'); -var uid = require('../util/uid'); -var specMap = require('./sb2specmap'); -var Variable = require('../engine/variable'); -var List = require('../engine/list'); +const Blocks = require('../engine/blocks'); +const RenderedTarget = require('../sprites/rendered-target'); +const Sprite = require('../sprites/sprite'); +const Color = require('../util/color'); +const log = require('../util/log'); +const uid = require('../util/uid'); +const specMap = require('./sb2specmap'); +const Variable = require('../engine/variable'); +const List = require('../engine/list'); -var loadCostume = require('./load-costume.js'); -var loadSound = require('./load-sound.js'); +const loadCostume = require('./load-costume.js'); +const loadSound = require('./load-sound.js'); /** * Parse a single "Scratch object" and create all its in-memory VM objects. @@ -32,19 +32,19 @@ var parseScratchObject = function (object, runtime, topLevel) { return null; } // Blocks container for this object. - var blocks = new Blocks(); + const blocks = new Blocks(); // @todo: For now, load all Scratch objects (stage/sprites) as a Sprite. - var sprite = new Sprite(blocks, runtime); + const sprite = new Sprite(blocks, runtime); // Sprite/stage name from JSON. if (object.hasOwnProperty('objName')) { sprite.name = object.objName; } // Costumes from JSON. - var costumePromises = []; + const costumePromises = []; if (object.hasOwnProperty('costumes')) { - for (var i = 0; i < object.costumes.length; i++) { - var costumeSource = object.costumes[i]; - var costume = { + for (let i = 0; i < object.costumes.length; i++) { + const costumeSource = object.costumes[i]; + const costume = { name: costumeSource.costumeName, bitmapResolution: costumeSource.bitmapResolution || 1, rotationCenterX: costumeSource.rotationCenterX, @@ -57,9 +57,9 @@ var parseScratchObject = function (object, runtime, topLevel) { } // Sounds from JSON if (object.hasOwnProperty('sounds')) { - for (var s = 0; s < object.sounds.length; s++) { - var soundSource = object.sounds[s]; - var sound = { + for (let s = 0; s < object.sounds.length; s++) { + const soundSource = object.sounds[s]; + const sound = { name: soundSource.soundName, format: soundSource.format, rate: soundSource.rate, @@ -77,13 +77,13 @@ var parseScratchObject = function (object, runtime, topLevel) { parseScripts(object.scripts, blocks); } // Create the first clone, and load its run-state from JSON. - var target = sprite.createClone(); + const target = sprite.createClone(); // Add it to the runtime's list of targets. runtime.targets.push(target); // Load target properties from JSON. if (object.hasOwnProperty('variables')) { - for (var j = 0; j < object.variables.length; j++) { - var variable = object.variables[j]; + for (let j = 0; j < object.variables.length; j++) { + const variable = object.variables[j]; target.variables[variable.name] = new Variable( variable.name, variable.value, @@ -92,8 +92,8 @@ var parseScratchObject = function (object, runtime, topLevel) { } } if (object.hasOwnProperty('lists')) { - for (var k = 0; k < object.lists.length; k++) { - var list = object.lists[k]; + for (let k = 0; k < object.lists.length; k++) { + const list = object.lists[k]; // @todo: monitor properties. target.lists[list.listName] = new List( list.listName, @@ -133,13 +133,13 @@ var parseScratchObject = function (object, runtime, topLevel) { } } target.isStage = topLevel; - Promise.all(costumePromises).then(function (costumes) { + Promise.all(costumePromises).then(costumes => { sprite.costumes = costumes; target.updateAllDrawableProperties(); }); // The stage will have child objects; recursively process them. if (object.children) { - for (var m = 0; m < object.children.length; m++) { + for (let m = 0; m < object.children.length; m++) { parseScratchObject(object.children[m], runtime, false); } } @@ -154,7 +154,7 @@ var parseScratchObject = function (object, runtime, topLevel) { * @param {boolean=} optForceSprite If set, treat as sprite (Sprite2). * @return {?Target} Top-level target created (stage or sprite). */ -var sb2import = function (json, runtime, optForceSprite) { +const sb2import = function (json, runtime, optForceSprite) { return parseScratchObject( JSON.parse(json), runtime, @@ -169,12 +169,12 @@ var sb2import = function (json, runtime, optForceSprite) { * @param {!Blocks} blocks Blocks object to load parsed blocks into. */ var parseScripts = function (scripts, blocks) { - for (var i = 0; i < scripts.length; i++) { - var script = scripts[i]; - var scriptX = script[0]; - var scriptY = script[1]; - var blockList = script[2]; - var parsedBlockList = parseBlockList(blockList); + for (let i = 0; i < scripts.length; i++) { + const script = scripts[i]; + const scriptX = script[0]; + const scriptY = script[1]; + const blockList = script[2]; + const parsedBlockList = parseBlockList(blockList); if (parsedBlockList[0]) { // Adjust script coordinates to account for // larger block size in scratch-blocks. @@ -185,8 +185,8 @@ var parseScripts = function (scripts, blocks) { parsedBlockList[0].parent = null; } // Flatten children and create add the blocks. - var convertedBlocks = flatten(parsedBlockList); - for (var j = 0; j < convertedBlocks.length; j++) { + const convertedBlocks = flatten(parsedBlockList); + for (let j = 0; j < convertedBlocks.length; j++) { blocks.createBlock(convertedBlocks[j]); } } @@ -201,11 +201,11 @@ var parseScripts = function (scripts, blocks) { * @return {Array.} Scratch VM-format block list. */ var parseBlockList = function (blockList) { - var resultingList = []; - var previousBlock = null; // For setting next. - for (var i = 0; i < blockList.length; i++) { - var block = blockList[i]; - var parsedBlock = parseBlock(block); + const resultingList = []; + let previousBlock = null; // For setting next. + for (let i = 0; i < blockList.length; i++) { + const block = blockList[i]; + const parsedBlock = parseBlock(block); if (typeof parsedBlock === 'undefined') continue; if (previousBlock) { parsedBlock.parent = previousBlock.id; @@ -224,9 +224,9 @@ var parseBlockList = function (blockList) { * @return {Array.} Flattened list to be passed to `blocks.createBlock`. */ var flatten = function (blocks) { - var finalBlocks = []; - for (var i = 0; i < blocks.length; i++) { - var block = blocks[i]; + let finalBlocks = []; + for (let i = 0; i < blocks.length; i++) { + const block = blocks[i]; finalBlocks.push(block); if (block.children) { finalBlocks = finalBlocks.concat(flatten(block.children)); @@ -243,19 +243,19 @@ var flatten = function (blocks) { * @param {string} procCode Scratch 2.0 procedure string. * @return {object} Argument map compatible with those in sb2specmap. */ -var parseProcedureArgMap = function (procCode) { - var argMap = [ +const parseProcedureArgMap = function (procCode) { + const argMap = [ {} // First item in list is op string. ]; - var INPUT_PREFIX = 'input'; - var inputCount = 0; + const INPUT_PREFIX = 'input'; + let 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(); + const parts = procCode.split(/(?=[^\\]%[nbs])/); + for (let i = 0; i < parts.length; i++) { + const part = parts[i].trim(); if (part.substring(0, 1) === '%') { - var argType = part.substring(1, 2); - var arg = { + const argType = part.substring(1, 2); + const arg = { type: 'input', inputName: INPUT_PREFIX + (inputCount++) }; @@ -277,15 +277,15 @@ var parseProcedureArgMap = function (procCode) { */ var parseBlock = function (sb2block) { // First item in block object is the old opcode (e.g., 'forward:'). - var oldOpcode = sb2block[0]; + const oldOpcode = sb2block[0]; // Convert the block using the specMap. See sb2specmap.js. if (!oldOpcode || !specMap[oldOpcode]) { log.warn('Couldn\'t find SB2 block: ', oldOpcode); return; } - var blockMetadata = specMap[oldOpcode]; + const blockMetadata = specMap[oldOpcode]; // Block skeleton. - var activeBlock = { + const activeBlock = { id: uid(), // Generate a new block unique ID. opcode: blockMetadata.opcode, // Converted, e.g. "motion_movesteps". inputs: {}, // Inputs to this block and the blocks they point to. @@ -301,15 +301,15 @@ var parseBlock = function (sb2block) { // 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. - for (var i = 0; i < blockMetadata.argMap.length; i++) { - var expectedArg = blockMetadata.argMap[i]; - var providedArg = sb2block[i + 1]; // (i = 0 is opcode) + for (let i = 0; i < blockMetadata.argMap.length; i++) { + const expectedArg = blockMetadata.argMap[i]; + const providedArg = sb2block[i + 1]; // (i = 0 is opcode) // Whether the input is obscuring a shadow. - var shadowObscured = false; + let shadowObscured = false; // Positional argument is an input. if (expectedArg.type === 'input') { // Create a new block and input metadata. - var inputUid = uid(); + const inputUid = uid(); activeBlock.inputs[expectedArg.inputName] = { name: expectedArg.inputName, block: null, @@ -325,8 +325,8 @@ var parseBlock = function (sb2block) { // Single block occupies the input. innerBlocks = [parseBlock(providedArg)]; } - var previousBlock = null; - for (var j = 0; j < innerBlocks.length; j++) { + let previousBlock = null; + for (let j = 0; j < innerBlocks.length; j++) { if (j === 0) { innerBlocks[j].parent = activeBlock.id; } else { @@ -350,9 +350,9 @@ var parseBlock = function (sb2block) { } // Each shadow has a field generated for it automatically. // Value to be filled in the field. - var fieldValue = providedArg; + let fieldValue = providedArg; // Shadows' field names match the input name, except for these: - var fieldName = expectedArg.inputName; + let fieldName = expectedArg.inputName; if (expectedArg.inputOp === 'math_number' || expectedArg.inputOp === 'math_whole_number' || expectedArg.inputOp === 'math_positive_number' || @@ -379,7 +379,7 @@ var parseBlock = function (sb2block) { // Filled drop-down menu. fieldValue = ''; } - var fields = {}; + const fields = {}; fields[fieldName] = { name: fieldName, value: fieldValue @@ -422,7 +422,7 @@ var parseBlock = function (sb2block) { } else if (oldOpcode === 'procDef') { // Mutation for procedure definition: // store all 2.0 proc data. - var procData = sb2block.slice(1); + const procData = sb2block.slice(1); activeBlock.mutation = { tagName: 'mutation', proccode: procData[0], // e.g., "abc %n %b %s" diff --git a/src/import/sb2specmap.js b/src/import/sb2specmap.js index 5ff49405..a16104f6 100644 --- a/src/import/sb2specmap.js +++ b/src/import/sb2specmap.js @@ -21,7 +21,7 @@ * properties. By hand, I matched the opcode name to the 3.0 opcode. * Finally, I filled in the expected arguments as below. */ -var specMap = { +const specMap = { 'forward:': { opcode: 'motion_movesteps', argMap: [ diff --git a/src/index.js b/src/index.js index e449758e..dfb56632 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,3 @@ -var VirtualMachine = require('./virtual-machine'); +const VirtualMachine = require('./virtual-machine'); module.exports = VirtualMachine; diff --git a/src/io/clock.js b/src/io/clock.js index aaefa9d5..6a28e30c 100644 --- a/src/io/clock.js +++ b/src/io/clock.js @@ -1,6 +1,6 @@ -var Timer = require('../util/timer'); +const Timer = require('../util/timer'); -var Clock = function (runtime) { +const Clock = function (runtime) { this._projectTimer = new Timer(); this._projectTimer.start(); this._pausedTime = null; @@ -26,7 +26,7 @@ Clock.prototype.pause = function () { Clock.prototype.resume = function () { this._paused = false; - var dt = this._projectTimer.timeElapsed() - this._pausedTime; + const dt = this._projectTimer.timeElapsed() - this._pausedTime; this._projectTimer.startTime += dt; }; diff --git a/src/io/keyboard.js b/src/io/keyboard.js index c7a21080..7fe46dc3 100644 --- a/src/io/keyboard.js +++ b/src/io/keyboard.js @@ -1,6 +1,6 @@ -var Cast = require('../util/cast'); +const Cast = require('../util/cast'); -var Keyboard = function (runtime) { +const Keyboard = function (runtime) { /** * List of currently pressed keys. * @type{Array.} @@ -25,7 +25,7 @@ Keyboard.prototype._scratchKeyToKeyCode = function (keyName) { // Key codes placed in with number blocks. return keyName; } - var keyString = Cast.toString(keyName); + const keyString = Cast.toString(keyName); switch (keyString) { case 'space': return 32; case 'left arrow': return 37; @@ -65,7 +65,7 @@ Keyboard.prototype._keyCodeToScratchKey = function (keyCode) { */ Keyboard.prototype.postData = function (data) { if (data.keyCode) { - var index = this._keysPressed.indexOf(data.keyCode); + const index = this._keysPressed.indexOf(data.keyCode); if (data.isDown) { // If not already present, add to the list. if (index < 0) { @@ -94,7 +94,7 @@ Keyboard.prototype.getKeyIsDown = function (key) { if (key === 'any') { return this._keysPressed.length > 0; } - var keyCode = this._scratchKeyToKeyCode(key); + const keyCode = this._scratchKeyToKeyCode(key); return this._keysPressed.indexOf(keyCode) > -1; }; diff --git a/src/io/mouse.js b/src/io/mouse.js index 4a7361fa..9d2721a2 100644 --- a/src/io/mouse.js +++ b/src/io/mouse.js @@ -1,6 +1,6 @@ -var MathUtil = require('../util/math-util'); +const MathUtil = require('../util/math-util'); -var Mouse = function (runtime) { +const Mouse = function (runtime) { this._x = 0; this._y = 0; this._isDown = false; @@ -20,9 +20,9 @@ var Mouse = function (runtime) { */ Mouse.prototype._activateClickHats = function (x, y) { if (this.runtime.renderer) { - var drawableID = this.runtime.renderer.pick(x, y); - for (var i = 0; i < this.runtime.targets.length; i++) { - var target = this.runtime.targets[i]; + const drawableID = this.runtime.renderer.pick(x, y); + for (let i = 0; i < this.runtime.targets.length; i++) { + const target = this.runtime.targets[i]; if (target.hasOwnProperty('drawableID') && target.drawableID === drawableID) { this.runtime.startHats('event_whenthisspriteclicked', diff --git a/src/playground/playground.js b/src/playground/playground.js index b37fda7e..81d6f746 100644 --- a/src/playground/playground.js +++ b/src/playground/playground.js @@ -1,10 +1,10 @@ -var Scratch = window.Scratch = window.Scratch || {}; +const Scratch = window.Scratch = window.Scratch || {}; -var ASSET_SERVER = 'https://cdn.assets.scratch.mit.edu/'; -var PROJECT_SERVER = 'https://cdn.projects.scratch.mit.edu/'; +const ASSET_SERVER = 'https://cdn.assets.scratch.mit.edu/'; +const PROJECT_SERVER = 'https://cdn.projects.scratch.mit.edu/'; -var loadProject = function () { - var id = location.hash.substring(1); +const loadProject = function () { + let id = location.hash.substring(1); if (id.length < 1 || !isFinite(id)) { id = '119615668'; } @@ -15,9 +15,9 @@ var loadProject = function () { * @param {Asset} asset - calculate a URL for this asset. * @returns {string} a URL to download a project file. */ -var getProjectUrl = function (asset) { - var assetIdParts = asset.assetId.split('.'); - var assetUrlParts = [PROJECT_SERVER, 'internalapi/project/', assetIdParts[0], '/get/']; +const getProjectUrl = function (asset) { + const assetIdParts = asset.assetId.split('.'); + const assetUrlParts = [PROJECT_SERVER, 'internalapi/project/', assetIdParts[0], '/get/']; if (assetIdParts[1]) { assetUrlParts.push(assetIdParts[1]); } @@ -28,8 +28,8 @@ var getProjectUrl = function (asset) { * @param {Asset} asset - calculate a URL for this asset. * @returns {string} a URL to download a project asset (PNG, WAV, etc.) */ -var getAssetUrl = function (asset) { - var assetUrlParts = [ +const getAssetUrl = function (asset) { + const assetUrlParts = [ ASSET_SERVER, 'internalapi/asset/', asset.assetId, @@ -43,32 +43,32 @@ var getAssetUrl = function (asset) { window.onload = function () { // Lots of global variables to make debugging easier // Instantiate the VM. - var vm = new window.VirtualMachine(); + const vm = new window.VirtualMachine(); Scratch.vm = vm; - var storage = new Scratch.Storage(); - var AssetType = Scratch.Storage.AssetType; + const storage = new Scratch.Storage(); + const AssetType = Scratch.Storage.AssetType; storage.addWebSource([AssetType.Project], getProjectUrl); storage.addWebSource([AssetType.ImageVector, AssetType.ImageBitmap, AssetType.Sound], getAssetUrl); vm.attachStorage(storage); // Loading projects from the server. document.getElementById('projectLoadButton').onclick = function () { - document.location = '#' + document.getElementById('projectId').value; + document.location = `#${document.getElementById('projectId').value}`; location.reload(); }; loadProject(); // Instantiate the renderer and connect it to the VM. - var canvas = document.getElementById('scratch-stage'); - var renderer = new window.RenderWebGL(canvas); + const canvas = document.getElementById('scratch-stage'); + const renderer = new window.RenderWebGL(canvas); Scratch.renderer = renderer; vm.attachRenderer(renderer); - var audioEngine = new window.AudioEngine(); + const audioEngine = new window.AudioEngine(); vm.attachAudioEngine(audioEngine); // Instantiate scratch-blocks and attach it to the DOM. - var workspace = window.Blockly.inject('blocks', { + const workspace = window.Blockly.inject('blocks', { media: './media/', zoom: { controls: true, @@ -90,27 +90,27 @@ window.onload = function () { // Attach scratch-blocks events to VM. workspace.addChangeListener(vm.blockListener); - var flyoutWorkspace = workspace.getFlyout().getWorkspace(); + const flyoutWorkspace = workspace.getFlyout().getWorkspace(); flyoutWorkspace.addChangeListener(vm.flyoutBlockListener); // Create FPS counter. - var stats = new window.Stats(); + const stats = new window.Stats(); document.getElementById('tab-renderexplorer').appendChild(stats.dom); stats.dom.style.position = 'relative'; stats.begin(); // Playground data tabs. // Block representation tab. - var blockexplorer = document.getElementById('blockexplorer'); - var updateBlockExplorer = function (blocks) { + const blockexplorer = document.getElementById('blockexplorer'); + const updateBlockExplorer = function (blocks) { blockexplorer.innerHTML = JSON.stringify(blocks, null, 2); window.hljs.highlightBlock(blockexplorer); }; // Thread representation tab. - var threadexplorer = document.getElementById('threadexplorer'); - var cachedThreadJSON = ''; - var updateThreadExplorer = function (newJSON) { + const threadexplorer = document.getElementById('threadexplorer'); + let cachedThreadJSON = ''; + const updateThreadExplorer = function (newJSON) { if (newJSON !== cachedThreadJSON) { cachedThreadJSON = newJSON; threadexplorer.innerHTML = cachedThreadJSON; @@ -129,28 +129,28 @@ window.onload = function () { // VM handlers. // Receipt of new playground data (thread, block representations). - vm.on('playgroundData', function (data) { + vm.on('playgroundData', data => { updateThreadExplorer(data.threads); updateBlockExplorer(data.blocks); }); // Receipt of new block XML for the selected target. - vm.on('workspaceUpdate', function (data) { + vm.on('workspaceUpdate', data => { workspace.clear(); - var dom = window.Blockly.Xml.textToDom(data.xml); + const dom = window.Blockly.Xml.textToDom(data.xml); window.Blockly.Xml.domToWorkspace(dom, workspace); }); // Receipt of new list of targets, selected target update. - var selectedTarget = document.getElementById('selectedTarget'); - vm.on('targetsUpdate', function (data) { + const selectedTarget = document.getElementById('selectedTarget'); + vm.on('targetsUpdate', data => { // Clear select box. while (selectedTarget.firstChild) { selectedTarget.removeChild(selectedTarget.firstChild); } // Generate new select box. - for (var i = 0; i < data.targetList.length; i++) { - var targetOption = document.createElement('option'); + for (let i = 0; i < data.targetList.length; i++) { + const targetOption = document.createElement('option'); targetOption.setAttribute('value', data.targetList[i].id); // If target id matches editingTarget id, select it. if (data.targetList[i].id === data.editingTarget) { @@ -167,23 +167,23 @@ window.onload = function () { }; // Feedback for stacks and blocks running. - vm.on('SCRIPT_GLOW_ON', function (data) { + vm.on('SCRIPT_GLOW_ON', data => { workspace.glowStack(data.id, true); }); - vm.on('SCRIPT_GLOW_OFF', function (data) { + vm.on('SCRIPT_GLOW_OFF', data => { workspace.glowStack(data.id, false); }); - vm.on('BLOCK_GLOW_ON', function (data) { + vm.on('BLOCK_GLOW_ON', data => { workspace.glowBlock(data.id, true); }); - vm.on('BLOCK_GLOW_OFF', function (data) { + vm.on('BLOCK_GLOW_OFF', data => { workspace.glowBlock(data.id, false); }); - vm.on('VISUAL_REPORT', function (data) { + vm.on('VISUAL_REPORT', data => { workspace.reportValue(data.id, data.value); }); - vm.on('SPRITE_INFO_REPORT', function (data) { + vm.on('SPRITE_INFO_REPORT', data => { if (data.id !== selectedTarget.value) return; // Not the editingTarget document.getElementById('sinfo-x').value = data.x; document.getElementById('sinfo-y').value = data.y; @@ -193,8 +193,8 @@ window.onload = function () { document.getElementById('sinfo-visible').value = data.visible; }); - document.getElementById('sinfo-post').addEventListener('click', function () { - var data = {}; + document.getElementById('sinfo-post').addEventListener('click', () => { + const data = {}; data.x = document.getElementById('sinfo-x').value; data.y = document.getElementById('sinfo-y').value; data.direction = document.getElementById('sinfo-direction').value; @@ -204,9 +204,9 @@ window.onload = function () { }); // Feed mouse events as VM I/O events. - document.addEventListener('mousemove', function (e) { - var rect = canvas.getBoundingClientRect(); - var coordinates = { + document.addEventListener('mousemove', e => { + const rect = canvas.getBoundingClientRect(); + const coordinates = { x: e.clientX - rect.left, y: e.clientY - rect.top, canvasWidth: rect.width, @@ -214,9 +214,9 @@ window.onload = function () { }; Scratch.vm.postIOData('mouse', coordinates); }); - canvas.addEventListener('mousedown', function (e) { - var rect = canvas.getBoundingClientRect(); - var data = { + canvas.addEventListener('mousedown', e => { + const rect = canvas.getBoundingClientRect(); + const data = { isDown: true, x: e.clientX - rect.left, y: e.clientY - rect.top, @@ -226,9 +226,9 @@ window.onload = function () { Scratch.vm.postIOData('mouse', data); e.preventDefault(); }); - canvas.addEventListener('mouseup', function (e) { - var rect = canvas.getBoundingClientRect(); - var data = { + canvas.addEventListener('mouseup', e => { + const rect = canvas.getBoundingClientRect(); + const data = { isDown: false, x: e.clientX - rect.left, y: e.clientY - rect.top, @@ -240,7 +240,7 @@ window.onload = function () { }); // Feed keyboard events as VM I/O events. - document.addEventListener('keydown', function (e) { + document.addEventListener('keydown', e => { // Don't capture keys intended for Blockly inputs. if (e.target !== document && e.target !== document.body) { return; @@ -251,7 +251,7 @@ window.onload = function () { }); e.preventDefault(); }); - document.addEventListener('keyup', function (e) { + document.addEventListener('keyup', e => { // Always capture up events, // even those that have switched to other targets. Scratch.vm.postIOData('keyboard', { @@ -275,29 +275,29 @@ window.onload = function () { requestAnimationFrame(animate); // Handlers for green flag and stop all. - document.getElementById('greenflag').addEventListener('click', function () { + document.getElementById('greenflag').addEventListener('click', () => { vm.greenFlag(); }); - document.getElementById('stopall').addEventListener('click', function () { + document.getElementById('stopall').addEventListener('click', () => { vm.stopAll(); }); - document.getElementById('turbomode').addEventListener('change', function () { - var turboOn = document.getElementById('turbomode').checked; + document.getElementById('turbomode').addEventListener('change', () => { + const turboOn = document.getElementById('turbomode').checked; vm.setTurboMode(turboOn); }); document.getElementById('compatmode').addEventListener('change', - function () { - var compatibilityMode = document.getElementById('compatmode').checked; + () => { + const compatibilityMode = document.getElementById('compatmode').checked; vm.setCompatibilityMode(compatibilityMode); }); - var tabBlockExplorer = document.getElementById('tab-blockexplorer'); - var tabThreadExplorer = document.getElementById('tab-threadexplorer'); - var tabRenderExplorer = document.getElementById('tab-renderexplorer'); - var tabImportExport = document.getElementById('tab-importexport'); + const tabBlockExplorer = document.getElementById('tab-blockexplorer'); + const tabThreadExplorer = document.getElementById('tab-threadexplorer'); + const tabRenderExplorer = document.getElementById('tab-renderexplorer'); + const tabImportExport = document.getElementById('tab-importexport'); // Handlers to show different explorers. document.getElementById('threadexplorer-link').addEventListener('click', - function () { + () => { Scratch.exploreTabOpen = true; getPlaygroundData(); tabBlockExplorer.style.display = 'none'; @@ -306,7 +306,7 @@ window.onload = function () { tabImportExport.style.display = 'none'; }); document.getElementById('blockexplorer-link').addEventListener('click', - function () { + () => { Scratch.exploreTabOpen = true; getPlaygroundData(); tabBlockExplorer.style.display = 'block'; @@ -315,7 +315,7 @@ window.onload = function () { tabImportExport.style.display = 'none'; }); document.getElementById('renderexplorer-link').addEventListener('click', - function () { + () => { Scratch.exploreTabOpen = false; tabBlockExplorer.style.display = 'none'; tabRenderExplorer.style.display = 'block'; @@ -323,7 +323,7 @@ window.onload = function () { tabImportExport.style.display = 'none'; }); document.getElementById('importexport-link').addEventListener('click', - function () { + () => { Scratch.exploreTabOpen = false; tabBlockExplorer.style.display = 'none'; tabRenderExplorer.style.display = 'none'; diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 29154af3..f6465da4 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -1,8 +1,8 @@ -var util = require('util'); +const util = require('util'); -var log = require('../util/log'); -var MathUtil = require('../util/math-util'); -var Target = require('../engine/target'); +const log = require('../util/log'); +const MathUtil = require('../util/math-util'); +const Target = require('../engine/target'); /** * Rendered target: instance of a sprite (clone), or the stage. @@ -10,7 +10,7 @@ var Target = require('../engine/target'); * @param {Runtime} runtime Reference to the runtime. * @constructor */ -var RenderedTarget = function (sprite, runtime) { +const RenderedTarget = function (sprite, runtime) { Target.call(this, sprite.blocks); this.runtime = runtime; /** @@ -175,10 +175,10 @@ RenderedTarget.prototype.rotationStyle = ( RenderedTarget.prototype.setXY = function (x, y, force) { if (this.isStage) return; if (this.dragging && !force) return; - var oldX = this.x; - var oldY = this.y; + const oldX = this.x; + const oldY = this.y; if (this.renderer) { - var position = this.renderer.getFencedPositionOfDrawable(this.drawableID, [x, y]); + const position = this.renderer.getFencedPositionOfDrawable(this.drawableID, [x, y]); this.x = position[0]; this.y = position[1]; @@ -202,15 +202,15 @@ RenderedTarget.prototype.setXY = function (x, y, force) { */ RenderedTarget.prototype._getRenderedDirectionAndScale = function () { // Default: no changes to `this.direction` or `this.scale`. - var finalDirection = this.direction; - var finalScale = [this.size, this.size]; + let finalDirection = this.direction; + let finalScale = [this.size, this.size]; if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) { // Force rendered direction to be 90. finalDirection = 90; } else if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) { // Force rendered direction to be 90, and flip drawable if needed. finalDirection = 90; - var scaleFlip = (this.direction < 0) ? -1 : 1; + const scaleFlip = (this.direction < 0) ? -1 : 1; finalScale = [scaleFlip * this.size, this.size]; } return {direction: finalDirection, scale: finalScale}; @@ -227,7 +227,7 @@ RenderedTarget.prototype.setDirection = function (direction) { // Keep direction between -179 and +180. this.direction = MathUtil.wrapClamp(direction, -179, 180); if (this.renderer) { - var renderedDirectionScale = this._getRenderedDirectionAndScale(); + const renderedDirectionScale = this._getRenderedDirectionAndScale(); this.renderer.updateDrawableProperties(this.drawableID, { direction: renderedDirectionScale.direction, scale: renderedDirectionScale.scale @@ -297,16 +297,16 @@ RenderedTarget.prototype.setSize = function (size) { if (this.renderer) { // Clamp to scales relative to costume and stage size. // See original ScratchSprite.as:setSize. - var costumeSize = this.renderer.getSkinSize(this.drawableID); - var origW = costumeSize[0]; - var origH = costumeSize[1]; - var minScale = Math.min(1, Math.max(5 / origW, 5 / origH)); - var maxScale = Math.min( + const costumeSize = this.renderer.getSkinSize(this.drawableID); + const origW = costumeSize[0]; + const origH = costumeSize[1]; + const minScale = Math.min(1, Math.max(5 / origW, 5 / origH)); + const maxScale = Math.min( (1.5 * this.runtime.constructor.STAGE_WIDTH) / origW, (1.5 * this.runtime.constructor.STAGE_HEIGHT) / origH ); this.size = MathUtil.clamp(size / 100, minScale, maxScale) * 100; - var renderedDirectionScale = this._getRenderedDirectionAndScale(); + const renderedDirectionScale = this._getRenderedDirectionAndScale(); this.renderer.updateDrawableProperties(this.drawableID, { direction: renderedDirectionScale.direction, scale: renderedDirectionScale.scale @@ -326,7 +326,7 @@ RenderedTarget.prototype.setEffect = function (effectName, value) { if (!this.effects.hasOwnProperty(effectName)) return; this.effects[effectName] = value; if (this.renderer) { - var props = {}; + const props = {}; props[effectName] = this.effects[effectName]; this.renderer.updateDrawableProperties(this.drawableID, props); if (this.visible) { @@ -339,7 +339,7 @@ RenderedTarget.prototype.setEffect = function (effectName, value) { * Clear all graphic effects on this rendered target. */ RenderedTarget.prototype.clearEffects = function () { - for (var effectName in this.effects) { + for (const effectName in this.effects) { if (!this.effects.hasOwnProperty(effectName)) continue; this.effects[effectName] = 0; } @@ -362,8 +362,8 @@ RenderedTarget.prototype.setCostume = function (index) { index, 0, this.sprite.costumes.length - 1 ); if (this.renderer) { - var costume = this.sprite.costumes[this.currentCostume]; - var drawableProperties = { + const costume = this.sprite.costumes[this.currentCostume]; + const drawableProperties = { skinId: costume.skinId, costumeResolution: costume.bitmapResolution }; @@ -371,7 +371,7 @@ RenderedTarget.prototype.setCostume = function (index) { typeof costume.rotationCenterX !== 'undefined' && typeof costume.rotationCenterY !== 'undefined' ) { - var scale = costume.bitmapResolution || 1; + const scale = costume.bitmapResolution || 1; drawableProperties.rotationCenter = [ costume.rotationCenterX / scale, costume.rotationCenterY / scale @@ -398,7 +398,7 @@ RenderedTarget.prototype.setRotationStyle = function (rotationStyle) { this.rotationStyle = RenderedTarget.ROTATION_STYLE_LEFT_RIGHT; } if (this.renderer) { - var renderedDirectionScale = this._getRenderedDirectionAndScale(); + const renderedDirectionScale = this._getRenderedDirectionAndScale(); this.renderer.updateDrawableProperties(this.drawableID, { direction: renderedDirectionScale.direction, scale: renderedDirectionScale.scale @@ -416,7 +416,7 @@ RenderedTarget.prototype.setRotationStyle = function (rotationStyle) { * @return {number} Index of the named costume, or -1 if not present. */ RenderedTarget.prototype.getCostumeIndexByName = function (costumeName) { - for (var i = 0; i < this.sprite.costumes.length; i++) { + for (let i = 0; i < this.sprite.costumes.length; i++) { if (this.sprite.costumes[i].name === costumeName) { return i; } @@ -454,10 +454,10 @@ RenderedTarget.prototype.getSounds = function () { */ RenderedTarget.prototype.updateAllDrawableProperties = function () { if (this.renderer) { - var renderedDirectionScale = this._getRenderedDirectionAndScale(); - var costume = this.sprite.costumes[this.currentCostume]; - var bitmapResolution = costume.bitmapResolution || 1; - var props = { + const renderedDirectionScale = this._getRenderedDirectionAndScale(); + const costume = this.sprite.costumes[this.currentCostume]; + const bitmapResolution = costume.bitmapResolution || 1; + const props = { position: [this.x, this.y], direction: renderedDirectionScale.direction, draggable: this.draggable, @@ -470,7 +470,7 @@ RenderedTarget.prototype.updateAllDrawableProperties = function () { costume.rotationCenterY / bitmapResolution ] }; - for (var effectName in this.effects) { + for (const effectName in this.effects) { if (!this.effects.hasOwnProperty(effectName)) continue; props[effectName] = this.effects[effectName]; } @@ -522,7 +522,7 @@ RenderedTarget.prototype.isTouchingPoint = function (x, y) { // @todo: Update once pick is in Scratch coordinates. // Limits test to this Drawable, so this will return true // even if the clone is obscured by another Drawable. - var pickResult = this.runtime.renderer.pick( + const pickResult = this.runtime.renderer.pick( x + (this.runtime.constructor.STAGE_WIDTH / 2), -y + (this.runtime.constructor.STAGE_HEIGHT / 2), null, null, @@ -539,9 +539,9 @@ RenderedTarget.prototype.isTouchingPoint = function (x, y) { */ RenderedTarget.prototype.isTouchingEdge = function () { if (this.renderer) { - var stageWidth = this.runtime.constructor.STAGE_WIDTH; - var stageHeight = this.runtime.constructor.STAGE_HEIGHT; - var bounds = this.getBounds(); + const stageWidth = this.runtime.constructor.STAGE_WIDTH; + const stageHeight = this.runtime.constructor.STAGE_HEIGHT; + const bounds = this.getBounds(); if (bounds.left < -stageWidth / 2 || bounds.right > stageWidth / 2 || bounds.top > stageHeight / 2 || @@ -558,13 +558,11 @@ RenderedTarget.prototype.isTouchingEdge = function () { * @return {boolean} True iff touching a clone of the sprite. */ RenderedTarget.prototype.isTouchingSprite = function (spriteName) { - var firstClone = this.runtime.getSpriteTargetByName(spriteName); + const firstClone = this.runtime.getSpriteTargetByName(spriteName); if (!firstClone || !this.renderer) { return false; } - var drawableCandidates = firstClone.sprite.clones.map(function (clone) { - return clone.drawableID; - }); + const drawableCandidates = firstClone.sprite.clones.map(clone => clone.drawableID); return this.renderer.isTouchingDrawables( this.drawableID, drawableCandidates); }; @@ -623,7 +621,7 @@ RenderedTarget.prototype.goBackLayers = function (nLayers) { */ RenderedTarget.prototype.goBehindOther = function (other) { if (this.renderer) { - var otherLayer = this.renderer.setDrawableOrder( + const otherLayer = this.renderer.setDrawableOrder( other.drawableID, 0, true); this.renderer.setDrawableOrder(this.drawableID, otherLayer); } @@ -637,7 +635,7 @@ RenderedTarget.prototype.goBehindOther = function (other) { * @return {Array.} Fenced X and Y coordinates. */ RenderedTarget.prototype.keepInFence = function (newX, newY, optFence) { - var fence = optFence; + let fence = optFence; if (!fence) { fence = { left: -this.runtime.constructor.STAGE_WIDTH / 2, @@ -646,7 +644,7 @@ RenderedTarget.prototype.keepInFence = function (newX, newY, optFence) { bottom: -this.runtime.constructor.STAGE_HEIGHT / 2 }; } - var bounds = this.getBounds(); + const bounds = this.getBounds(); if (!bounds) return; // Adjust the known bounds to the target position. bounds.left += (newX - this.x); @@ -654,8 +652,8 @@ RenderedTarget.prototype.keepInFence = function (newX, newY, optFence) { bounds.top += (newY - this.y); bounds.bottom += (newY - this.y); // Find how far we need to move the target position. - var dx = 0; - var dy = 0; + let dx = 0; + let dy = 0; if (bounds.left < fence.left) { dx += fence.left - bounds.left; } @@ -681,7 +679,7 @@ RenderedTarget.prototype.makeClone = function () { return null; // Hit max clone limit, or this is the stage. } this.runtime.changeCloneCounter(1); - var newClone = this.sprite.createClone(); + const newClone = this.sprite.createClone(); // Copy all properties. newClone.x = this.x; newClone.y = this.y; @@ -726,7 +724,7 @@ RenderedTarget.prototype.onStopAll = function () { * @param {object} data An object with sprite info data to set. */ RenderedTarget.prototype.postSpriteInfo = function (data) { - var force = data.hasOwnProperty('force') ? data.force : null; + const force = data.hasOwnProperty('force') ? data.force : null; if (data.hasOwnProperty('x')) { this.setXY(data.x, this.y, force); } diff --git a/src/sprites/sprite.js b/src/sprites/sprite.js index 8f341987..a6b00c2e 100644 --- a/src/sprites/sprite.js +++ b/src/sprites/sprite.js @@ -1,5 +1,5 @@ -var RenderedTarget = require('./rendered-target'); -var Blocks = require('../engine/blocks'); +const RenderedTarget = require('./rendered-target'); +const Blocks = require('../engine/blocks'); /** * Sprite to be used on the Scratch stage. @@ -8,7 +8,7 @@ var Blocks = require('../engine/blocks'); * @param {Runtime} runtime Reference to the runtime. * @constructor */ -var Sprite = function (blocks, runtime) { +const Sprite = function (blocks, runtime) { this.runtime = runtime; if (!blocks) { // Shared set of blocks for all clones. @@ -49,7 +49,7 @@ var Sprite = function (blocks, runtime) { * @returns {!RenderedTarget} Newly created clone. */ Sprite.prototype.createClone = function () { - var newClone = new RenderedTarget(this, this.runtime); + const newClone = new RenderedTarget(this, this.runtime); newClone.isOriginal = this.clones.length === 0; this.clones.push(newClone); if (newClone.isOriginal) { diff --git a/src/util/cast.js b/src/util/cast.js index 79e76b13..0d4f26db 100644 --- a/src/util/cast.js +++ b/src/util/cast.js @@ -1,6 +1,6 @@ -var Color = require('../util/color'); +const Color = require('../util/color'); -var Cast = function () {}; +const Cast = function () {}; /** * @fileoverview @@ -21,7 +21,7 @@ var Cast = function () {}; * @return {number} The Scratch-casted number value. */ Cast.toNumber = function (value) { - var n = Number(value); + const n = Number(value); if (isNaN(n)) { // Scratch treats NaN as 0, when needed as a number. // E.g., 0 + NaN -> 0. @@ -71,7 +71,7 @@ Cast.toString = function (value) { * @return {Array.} [r,g,b], values between 0-255. */ Cast.toRgbColorList = function (value) { - var color = Cast.toRgbColorObject(value); + const color = Cast.toRgbColorObject(value); return [color.r, color.g, color.b]; }; @@ -81,7 +81,7 @@ Cast.toRgbColorList = function (value) { * @return {RGBOject} [r,g,b], values between 0-255. */ Cast.toRgbColorObject = function (value) { - var color; + let color; if (typeof value === 'string' && value.substring(0, 1) === '#') { color = Color.hexToRgb(value); } else { @@ -107,8 +107,8 @@ Cast.isWhiteSpace = function (val) { * @returns {number} Negative number if v1 < v2; 0 if equal; positive otherwise. */ Cast.compare = function (v1, v2) { - var n1 = Number(v1); - var n2 = Number(v2); + let n1 = Number(v1); + let n2 = Number(v2); if (n1 === 0 && Cast.isWhiteSpace(v1)) { n1 = NaN; } else if (n2 === 0 && Cast.isWhiteSpace(v2)) { @@ -117,13 +117,13 @@ Cast.compare = function (v1, v2) { if (isNaN(n1) || isNaN(n2)) { // At least one argument can't be converted to a number. // Scratch compares strings as case insensitive. - var s1 = String(v1).toLowerCase(); - var s2 = String(v2).toLowerCase(); + const s1 = String(v1).toLowerCase(); + const s2 = String(v2).toLowerCase(); return s1.localeCompare(s2); - } else { - // Compare as numbers. - return n1 - n2; } + // Compare as numbers. + return n1 - n2; + }; /** diff --git a/src/util/clone.js b/src/util/clone.js index 5e1011a2..035cadc6 100644 --- a/src/util/clone.js +++ b/src/util/clone.js @@ -2,7 +2,7 @@ * Methods for cloning JavaScript objects. * @type {object} */ -var Clone = {}; +const Clone = {}; /** * Deep-clone a "simple" object: one which can be fully expressed with JSON. diff --git a/src/util/color.js b/src/util/color.js index 11082d66..1229e0ea 100644 --- a/src/util/color.js +++ b/src/util/color.js @@ -1,4 +1,4 @@ -var Color = function () {}; +const Color = function () {}; /** * @typedef {object} RGBObject - An object representing a color in RGB format. @@ -29,8 +29,8 @@ Color.decimalToHex = function (decimal) { if (decimal < 0) { decimal += 0xFFFFFF + 1; } - var hex = Number(decimal).toString(16); - hex = '#' + '000000'.substr(0, 6 - hex.length) + hex; + let hex = Number(decimal).toString(16); + hex = `#${'000000'.substr(0, 6 - hex.length)}${hex}`; return hex; }; @@ -40,10 +40,10 @@ Color.decimalToHex = function (decimal) { * @return {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}. */ Color.decimalToRgb = function (decimal) { - var a = (decimal >> 24) & 0xFF; - var r = (decimal >> 16) & 0xFF; - var g = (decimal >> 8) & 0xFF; - var b = decimal & 0xFF; + const a = (decimal >> 24) & 0xFF; + const r = (decimal >> 16) & 0xFF; + const g = (decimal >> 8) & 0xFF; + const b = decimal & 0xFF; return {r: r, g: g, b: b, a: a > 0 ? a : 255}; }; @@ -55,11 +55,9 @@ Color.decimalToRgb = function (decimal) { * @return {RGBObject} null on failure, or rgb: {r: red [0,255], g: green [0,255], b: blue [0,255]}. */ Color.hexToRgb = function (hex) { - var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; - hex = hex.replace(shorthandRegex, function (m, r, g, b) { - return r + r + g + g + b + b; - }); - var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + hex = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b); + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), @@ -100,20 +98,20 @@ Color.hexToDecimal = function (hex) { * @return {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}. */ Color.hsvToRgb = function (hsv) { - var h = hsv.h % 360; + let h = hsv.h % 360; if (h < 0) h += 360; - var s = Math.max(0, Math.min(hsv.s, 1)); - var v = Math.max(0, Math.min(hsv.v, 1)); + const s = Math.max(0, Math.min(hsv.s, 1)); + const v = Math.max(0, Math.min(hsv.v, 1)); - var i = Math.floor(h / 60); - var f = (h / 60) - i; - var p = v * (1 - s); - var q = v * (1 - (s * f)); - var t = v * (1 - (s * (1 - f))); + const i = Math.floor(h / 60); + const f = (h / 60) - i; + const p = v * (1 - s); + const q = v * (1 - (s * f)); + const t = v * (1 - (s * (1 - f))); - var r; - var g; - var b; + let r; + let g; + let b; switch (i) { default: @@ -162,18 +160,18 @@ Color.hsvToRgb = function (hsv) { * @return {HSVObject} hsv - {h: hue [0,360), s: saturation [0,1], v: value [0,1]} */ Color.rgbToHsv = function (rgb) { - var r = rgb.r / 255; - var g = rgb.g / 255; - var b = rgb.b / 255; - var x = Math.min(Math.min(r, g), b); - var v = Math.max(Math.max(r, g), b); + const r = rgb.r / 255; + const g = rgb.g / 255; + const b = rgb.b / 255; + const x = Math.min(Math.min(r, g), b); + const v = Math.max(Math.max(r, g), b); // For grays, hue will be arbitrarily reported as zero. Otherwise, calculate - var h = 0; - var s = 0; + let h = 0; + let s = 0; if (x !== v) { - var f = (r === x) ? g - b : ((g === x) ? b - r : r - g); - var i = (r === x) ? 3 : ((g === x) ? 5 : 1); + const f = (r === x) ? g - b : ((g === x) ? b - r : r - g); + const i = (r === x) ? 3 : ((g === x) ? 5 : 1); h = ((i - (f / (v - x))) * 60) % 360; s = (v - x) / v; } @@ -191,7 +189,7 @@ Color.rgbToHsv = function (rgb) { Color.mixRgb = function (rgb0, rgb1, fraction1) { if (fraction1 <= 0) return rgb0; if (fraction1 >= 1) return rgb1; - var fraction0 = 1 - fraction1; + const fraction0 = 1 - fraction1; return { r: (fraction0 * rgb0.r) + (fraction1 * rgb1.r), g: (fraction0 * rgb0.g) + (fraction1 * rgb1.g), diff --git a/src/util/log.js b/src/util/log.js index 28e596a1..cdc04520 100644 --- a/src/util/log.js +++ b/src/util/log.js @@ -1,4 +1,4 @@ -var minilog = require('minilog'); +const minilog = require('minilog'); minilog.enable(); module.exports = minilog('vm'); diff --git a/src/util/math-util.js b/src/util/math-util.js index 6191fdec..b92452b1 100644 --- a/src/util/math-util.js +++ b/src/util/math-util.js @@ -1,4 +1,4 @@ -var MathUtil = function () {}; +const MathUtil = function () {}; /** * Convert a value from degrees to radians. @@ -41,7 +41,7 @@ MathUtil.clamp = function (n, min, max) { * @return {!number} Value of n wrapped between min and max. */ MathUtil.wrapClamp = function (n, min, max) { - var range = (max - min) + 1; + const range = (max - min) + 1; return n - (Math.floor((n - min) / range) * range); }; diff --git a/src/util/string-util.js b/src/util/string-util.js index 6270cd88..ed40a2e6 100644 --- a/src/util/string-util.js +++ b/src/util/string-util.js @@ -1,7 +1,7 @@ -var StringUtil = function () {}; +const StringUtil = function () {}; StringUtil.withoutTrailingDigits = function (s) { - var i = s.length - 1; + let i = s.length - 1; while ((i >= 0) && ('0123456789'.indexOf(s.charAt(i)) > -1)) i--; return s.slice(0, i + 1); }; @@ -9,7 +9,7 @@ StringUtil.withoutTrailingDigits = function (s) { StringUtil.unusedName = function (name, existingNames) { if (existingNames.indexOf(name) < 0) return name; name = StringUtil.withoutTrailingDigits(name); - var i = 2; + let i = 2; while (existingNames.indexOf(name + i) >= 0) i++; return name + i; }; diff --git a/src/util/timer.js b/src/util/timer.js index 9a60e568..79a1852b 100644 --- a/src/util/timer.js +++ b/src/util/timer.js @@ -15,7 +15,7 @@ /** * @constructor */ -var Timer = function () {}; +const Timer = function () {}; /** * Used to store the start time of a timer action. @@ -28,13 +28,13 @@ Timer.prototype.startTime = 0; * However, instancing it like below (caching the self.performance to a local variable) negates most of the issues. * @type {boolean} */ -var USE_PERFORMANCE = false; +const USE_PERFORMANCE = false; /** * Legacy object to allow for us to call now to get the old style date time (for backwards compatibility) * @deprecated This is only called via the nowObj.now() if no other means is possible... */ -var legacyDateCode = { +const legacyDateCode = { now: function () { return new Date().getTime(); } @@ -43,7 +43,7 @@ var legacyDateCode = { /** * Use this object to route all time functions through single access points. */ -var nowObj = (USE_PERFORMANCE && typeof self !== 'undefined' && self.performance && 'now' in self.performance) ? +const nowObj = (USE_PERFORMANCE && typeof self !== 'undefined' && self.performance && 'now' in self.performance) ? self.performance : Date.now ? Date : legacyDateCode; /** diff --git a/src/util/uid.js b/src/util/uid.js index 532f9e9b..fd4c41e8 100644 --- a/src/util/uid.js +++ b/src/util/uid.js @@ -8,7 +8,7 @@ * Removed $ due to issue 251. * @private */ -var soup_ = '!#%()*+,-./:;=?@[]^_`{|}~' + +const soup_ = '!#%()*+,-./:;=?@[]^_`{|}~' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; /** @@ -16,11 +16,11 @@ var soup_ = '!#%()*+,-./:;=?@[]^_`{|}~' + * 87 characters ^ 20 length > 128 bits (better than a UUID). * @return {string} A globally unique ID string. */ -var uid = function () { - var length = 20; - var soupLength = soup_.length; - var id = []; - for (var i = 0; i < length; i++) { +const uid = function () { + const length = 20; + const soupLength = soup_.length; + const id = []; + for (let i = 0; i < length; i++) { id[i] = soup_.charAt(Math.random() * soupLength); } return id.join(''); diff --git a/src/util/xml-escape.js b/src/util/xml-escape.js index 00ce5bff..66daf83b 100644 --- a/src/util/xml-escape.js +++ b/src/util/xml-escape.js @@ -6,8 +6,8 @@ * @param {!string} unsafe Unsafe string. * @return {string} XML-escaped string, for use within an XML tag. */ -var xmlEscape = function (unsafe) { - return unsafe.replace(/[<>&'"]/g, function (c) { +const xmlEscape = function (unsafe) { + return unsafe.replace(/[<>&'"]/g, c => { switch (c) { case '<': return '<'; case '>': return '>'; diff --git a/src/virtual-machine.js b/src/virtual-machine.js index b85705e3..845dd329 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -1,25 +1,25 @@ -var EventEmitter = require('events'); -var util = require('util'); +const EventEmitter = require('events'); +const util = require('util'); -var log = require('./util/log'); -var Runtime = require('./engine/runtime'); -var ScratchStorage = require('scratch-storage'); -var sb2import = require('./import/sb2import'); -var StringUtil = require('./util/string-util'); +const log = require('./util/log'); +const Runtime = require('./engine/runtime'); +const ScratchStorage = require('scratch-storage'); +const sb2import = require('./import/sb2import'); +const StringUtil = require('./util/string-util'); -var loadCostume = require('./import/load-costume.js'); -var loadSound = require('./import/load-sound.js'); +const loadCostume = require('./import/load-costume.js'); +const loadSound = require('./import/load-sound.js'); -var RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; +const RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; -var AssetType = ScratchStorage.AssetType; +const AssetType = ScratchStorage.AssetType; /** * Handles connections between blocks, stage, and extensions. * @constructor */ -var VirtualMachine = function () { - var instance = this; +const VirtualMachine = function () { + const instance = this; // Bind event emitter and runtime to VM instance EventEmitter.call(instance); /** @@ -34,28 +34,28 @@ var VirtualMachine = function () { */ instance.editingTarget = null; // Runtime emits are passed along as VM emits. - instance.runtime.on(Runtime.SCRIPT_GLOW_ON, function (glowData) { + instance.runtime.on(Runtime.SCRIPT_GLOW_ON, glowData => { instance.emit(Runtime.SCRIPT_GLOW_ON, glowData); }); - instance.runtime.on(Runtime.SCRIPT_GLOW_OFF, function (glowData) { + instance.runtime.on(Runtime.SCRIPT_GLOW_OFF, glowData => { instance.emit(Runtime.SCRIPT_GLOW_OFF, glowData); }); - instance.runtime.on(Runtime.BLOCK_GLOW_ON, function (glowData) { + instance.runtime.on(Runtime.BLOCK_GLOW_ON, glowData => { instance.emit(Runtime.BLOCK_GLOW_ON, glowData); }); - instance.runtime.on(Runtime.BLOCK_GLOW_OFF, function (glowData) { + instance.runtime.on(Runtime.BLOCK_GLOW_OFF, glowData => { instance.emit(Runtime.BLOCK_GLOW_OFF, glowData); }); - instance.runtime.on(Runtime.PROJECT_RUN_START, function () { + instance.runtime.on(Runtime.PROJECT_RUN_START, () => { instance.emit(Runtime.PROJECT_RUN_START); }); - instance.runtime.on(Runtime.PROJECT_RUN_STOP, function () { + instance.runtime.on(Runtime.PROJECT_RUN_STOP, () => { instance.emit(Runtime.PROJECT_RUN_STOP); }); - instance.runtime.on(Runtime.VISUAL_REPORT, function (visualReport) { + instance.runtime.on(Runtime.VISUAL_REPORT, visualReport => { instance.emit(Runtime.VISUAL_REPORT, visualReport); }); - instance.runtime.on(Runtime.SPRITE_INFO_REPORT, function (spriteInfo) { + instance.runtime.on(Runtime.SPRITE_INFO_REPORT, spriteInfo => { instance.emit(Runtime.SPRITE_INFO_REPORT, spriteInfo); }); @@ -120,13 +120,11 @@ VirtualMachine.prototype.clear = function () { * Get data for playground. Data comes back in an emitted event. */ VirtualMachine.prototype.getPlaygroundData = function () { - var instance = this; + const instance = this; // Only send back thread data for the current editingTarget. - var threadData = this.runtime.threads.filter(function (thread) { - return thread.target === instance.editingTarget; - }); + const threadData = this.runtime.threads.filter(thread => thread.target === instance.editingTarget); // Remove the target key, since it's a circular reference. - var filteredThreadData = JSON.stringify(threadData, function (key, value) { + const filteredThreadData = JSON.stringify(threadData, (key, value) => { if (key === 'target') return; return value; }, 2); @@ -172,9 +170,9 @@ VirtualMachine.prototype.downloadProjectId = function (id) { log.error('No storage module present; cannot load project: ', id); return; } - var vm = this; - var promise = this.runtime.storage.load(AssetType.Project, id); - promise.then(function (projectAsset) { + const vm = this; + const promise = this.runtime.storage.load(AssetType.Project, id); + promise.then(projectAsset => { vm.loadProject(projectAsset.decodeText()); }); }; @@ -202,12 +200,12 @@ VirtualMachine.prototype.addSprite2 = function (json) { * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume. */ VirtualMachine.prototype.addCostume = function (md5ext, costumeObject) { - loadCostume(md5ext, costumeObject, this.runtime).then(function () { + loadCostume(md5ext, costumeObject, this.runtime).then(() => { this.editingTarget.sprite.costumes.push(costumeObject); this.editingTarget.setCostume( this.editingTarget.sprite.costumes.length - 1 ); - }.bind(this)); + }); }; /** @@ -216,10 +214,10 @@ VirtualMachine.prototype.addCostume = function (md5ext, costumeObject) { * @returns {?Promise} - a promise that resolves when the sound has been decoded and added */ VirtualMachine.prototype.addSound = function (soundObject) { - return loadSound(soundObject, this.runtime).then(function () { + return loadSound(soundObject, this.runtime).then(() => { this.editingTarget.sprite.sounds.push(soundObject); this.emitTargetsUpdate(); - }.bind(this)); + }); }; /** @@ -232,11 +230,11 @@ VirtualMachine.prototype.addSound = function (soundObject) { * @property {number} [bitmapResolution] - the resolution scale for a bitmap backdrop. */ VirtualMachine.prototype.addBackdrop = function (md5ext, backdropObject) { - loadCostume(md5ext, backdropObject, this.runtime).then(function () { - var stage = this.runtime.getTargetForStage(); + loadCostume(md5ext, backdropObject, this.runtime).then(() => { + const stage = this.runtime.getTargetForStage(); stage.sprite.costumes.push(backdropObject); stage.setCostume(stage.sprite.costumes.length - 1); - }.bind(this)); + }); }; /** @@ -245,21 +243,17 @@ VirtualMachine.prototype.addBackdrop = function (md5ext, backdropObject) { * @param {string} newName New name of the sprite. */ VirtualMachine.prototype.renameSprite = function (targetId, newName) { - var target = this.runtime.getTargetById(targetId); + const target = this.runtime.getTargetById(targetId); if (target) { if (!target.isSprite()) { throw new Error('Cannot rename non-sprite targets.'); } - var sprite = target.sprite; + const sprite = target.sprite; if (!sprite) { throw new Error('No sprite associated with this target.'); } if (newName && RESERVED_NAMES.indexOf(newName) === -1) { - var names = this.runtime.targets.filter(function (runtimeTarget) { - return runtimeTarget.isSprite(); - }).map(function (runtimeTarget) { - return runtimeTarget.sprite.name; - }); + const names = this.runtime.targets.filter(runtimeTarget => runtimeTarget.isSprite()).map(runtimeTarget => runtimeTarget.sprite.name); sprite.name = StringUtil.unusedName(newName, names); } @@ -274,18 +268,18 @@ VirtualMachine.prototype.renameSprite = function (targetId, newName) { * @param {string} targetId ID of a target whose sprite to delete. */ VirtualMachine.prototype.deleteSprite = function (targetId) { - var target = this.runtime.getTargetById(targetId); + const target = this.runtime.getTargetById(targetId); if (target) { if (!target.isSprite()) { throw new Error('Cannot delete non-sprite targets.'); } - var sprite = target.sprite; + const sprite = target.sprite; if (!sprite) { throw new Error('No sprite associated with this target.'); } - var currentEditingTarget = this.editingTarget; - for (var i = 0; i < sprite.clones.length; i++) { - var clone = sprite.clones[i]; + const currentEditingTarget = this.editingTarget; + for (let i = 0; i < sprite.clones.length; i++) { + const clone = sprite.clones[i]; this.runtime.stopForTarget(sprite.clones[i]); this.runtime.disposeTarget(sprite.clones[i]); // Ensure editing target is switched if we are deleting it. @@ -355,7 +349,7 @@ VirtualMachine.prototype.setEditingTarget = function (targetId) { if (targetId === this.editingTarget.id) { return; } - var target = this.runtime.getTargetById(targetId); + const target = this.runtime.getTargetById(targetId); if (target) { this.editingTarget = target; // Emit appropriate UI updates. @@ -373,12 +367,9 @@ VirtualMachine.prototype.setEditingTarget = function (targetId) { VirtualMachine.prototype.emitTargetsUpdate = function () { this.emit('targetsUpdate', { // [[target id, human readable target name], ...]. - targetList: this.runtime.targets.filter(function (target) { + targetList: this.runtime.targets.filter(target => // Don't report clones. - return !target.hasOwnProperty('isOriginal') || target.isOriginal; - }).map(function (target) { - return target.toJSON(); - }), + !target.hasOwnProperty('isOriginal') || target.isOriginal).map(target => target.toJSON()), // Currently editing target id. editingTarget: this.editingTarget ? this.editingTarget.id : null }); @@ -400,7 +391,7 @@ VirtualMachine.prototype.emitWorkspaceUpdate = function () { * @returns {?string} The target id, if found. Will also be null if the target found is the stage. */ VirtualMachine.prototype.getTargetIdForDrawableId = function (drawableId) { - var target = this.runtime.getTargetByDrawableId(drawableId); + const target = this.runtime.getTargetByDrawableId(drawableId); if (target && target.hasOwnProperty('id') && target.hasOwnProperty('isStage') && !target.isStage) { return target.id; } @@ -413,7 +404,7 @@ VirtualMachine.prototype.getTargetIdForDrawableId = function (drawableId) { * @param {string} targetId The id for the target to put into a drag state */ VirtualMachine.prototype.startDrag = function (targetId) { - var target = this.runtime.getTargetById(targetId); + const target = this.runtime.getTargetById(targetId); if (target) { target.startDrag(); this.setEditingTarget(target.id); @@ -425,7 +416,7 @@ VirtualMachine.prototype.startDrag = function (targetId) { * @param {string} targetId The id for the target to remove from the drag state */ VirtualMachine.prototype.stopDrag = function (targetId) { - var target = this.runtime.getTargetById(targetId); + const target = this.runtime.getTargetById(targetId); if (target) target.stopDrag(); }; From 4455b0694fcf6c9b99cbb352520cfc664b2f2bb1 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 17 Apr 2017 17:15:19 -0400 Subject: [PATCH 292/721] Fix remaining no-var and no-use-before-define's --- src/engine/adapter.js | 170 +++++++++++++------------ src/engine/blocks.js | 5 +- src/engine/execute.js | 8 +- src/engine/mutation-adapter.js | 2 +- src/import/load-costume.js | 5 +- src/import/sb2import.js | 223 +++++++++++++++++---------------- src/playground/playground.js | 4 +- src/virtual-machine.js | 4 +- 8 files changed, 218 insertions(+), 203 deletions(-) diff --git a/src/engine/adapter.js b/src/engine/adapter.js index 68a098c6..e664770a 100644 --- a/src/engine/adapter.js +++ b/src/engine/adapter.js @@ -1,49 +1,6 @@ const mutationAdapter = require('./mutation-adapter'); const html = require('htmlparser2'); -/** - * Convert outer blocks DOM from a Blockly CREATE event - * to a usable form for the Scratch runtime. - * This structure is based on Blockly xml.js:`domToWorkspace` and `domToBlock`. - * @param {Element} blocksDOM DOM tree for this event. - * @return {Array.} Usable list of blocks from this CREATE event. - */ -const domToBlocks = function (blocksDOM) { - // At this level, there could be multiple blocks adjacent in the DOM tree. - const blocks = {}; - for (let i = 0; i < blocksDOM.length; i++) { - const block = blocksDOM[i]; - if (!block.name || !block.attribs) { - continue; - } - const tagName = block.name.toLowerCase(); - if (tagName === 'block' || tagName === 'shadow') { - domToBlock(block, blocks, true, null); - } - } - // Flatten blocks object into a list. - const blocksList = []; - for (const b in blocks) { - if (!blocks.hasOwnProperty(b)) continue; - blocksList.push(blocks[b]); - } - return blocksList; -}; - -/** - * Adapter between block creation events and block representation which can be - * used by the Scratch runtime. - * @param {object} e `Blockly.events.create` - * @return {Array.} List of blocks from this CREATE event. - */ -const adapter = function (e) { - // Validate input - if (typeof e !== 'object') return; - if (typeof e.xml !== 'object') return; - - return domToBlocks(html.parseDOM(e.xml.outerHTML)); -}; - /** * Convert and an individual block DOM to the representation tree. * Based on Blockly's `domToBlockHeadless_`. @@ -53,7 +10,7 @@ const adapter = function (e) { * @param {?string} parent Parent block ID. * @return {undefined} */ -var domToBlock = function (blockDOM, blocks, isTopBlock, parent) { +const domToBlock = function (blockDOM, blocks, isTopBlock, parent) { // Block skeleton. const block = { id: blockDOM.attribs.id, // Block ID @@ -100,52 +57,103 @@ var domToBlock = function (blockDOM, blocks, isTopBlock, parent) { // as we won't be using all of them for Scratch. switch (xmlChild.name.toLowerCase()) { case 'field': - // Add the field to this block. - var fieldName = xmlChild.attribs.name; - var fieldData = ''; - if (xmlChild.children.length > 0 && xmlChild.children[0].data) { - fieldData = xmlChild.children[0].data; - } else { - // If the child of the field with a data property - // doesn't exist, set the data to an empty string. - fieldData = ''; + { + // Add the field to this block. + const fieldName = xmlChild.attribs.name; + let fieldData = ''; + if (xmlChild.children.length > 0 && xmlChild.children[0].data) { + fieldData = xmlChild.children[0].data; + } else { + // If the child of the field with a data property + // doesn't exist, set the data to an empty string. + fieldData = ''; + } + block.fields[fieldName] = { + name: fieldName, + value: fieldData + }; + break; } - block.fields[fieldName] = { - name: fieldName, - value: fieldData - }; - break; case 'value': case 'statement': - // Recursively generate block structure for input block. - domToBlock(childBlockNode, blocks, false, block.id); - if (childShadowNode && childBlockNode !== childShadowNode) { - // Also generate the shadow block. - domToBlock(childShadowNode, blocks, false, block.id); + { + // Recursively generate block structure for input block. + domToBlock(childBlockNode, blocks, false, block.id); + if (childShadowNode && childBlockNode !== childShadowNode) { + // Also generate the shadow block. + domToBlock(childShadowNode, blocks, false, block.id); + } + // Link this block's input to the child block. + const inputName = xmlChild.attribs.name; + block.inputs[inputName] = { + name: inputName, + block: childBlockNode.attribs.id, + shadow: childShadowNode ? childShadowNode.attribs.id : null + }; + break; } - // Link this block's input to the child block. - var inputName = xmlChild.attribs.name; - block.inputs[inputName] = { - name: inputName, - block: childBlockNode.attribs.id, - shadow: childShadowNode ? childShadowNode.attribs.id : null - }; - break; case 'next': - if (!childBlockNode || !childBlockNode.attribs) { - // Invalid child block. - continue; + { + if (!childBlockNode || !childBlockNode.attribs) { + // Invalid child block. + continue; + } + // Recursively generate block structure for next block. + domToBlock(childBlockNode, blocks, false, block.id); + // Link next block to this block. + block.next = childBlockNode.attribs.id; + break; } - // Recursively generate block structure for next block. - domToBlock(childBlockNode, blocks, false, block.id); - // Link next block to this block. - block.next = childBlockNode.attribs.id; - break; case 'mutation': - block.mutation = mutationAdapter(xmlChild); - break; + { + block.mutation = mutationAdapter(xmlChild); + break; + } } } }; +/** + * Convert outer blocks DOM from a Blockly CREATE event + * to a usable form for the Scratch runtime. + * This structure is based on Blockly xml.js:`domToWorkspace` and `domToBlock`. + * @param {Element} blocksDOM DOM tree for this event. + * @return {Array.} Usable list of blocks from this CREATE event. + */ +const domToBlocks = function (blocksDOM) { + // At this level, there could be multiple blocks adjacent in the DOM tree. + const blocks = {}; + for (let i = 0; i < blocksDOM.length; i++) { + const block = blocksDOM[i]; + if (!block.name || !block.attribs) { + continue; + } + const tagName = block.name.toLowerCase(); + if (tagName === 'block' || tagName === 'shadow') { + domToBlock(block, blocks, true, null); + } + } + // Flatten blocks object into a list. + const blocksList = []; + for (const b in blocks) { + if (!blocks.hasOwnProperty(b)) continue; + blocksList.push(blocks[b]); + } + return blocksList; +}; + +/** + * Adapter between block creation events and block representation which can be + * used by the Scratch runtime. + * @param {object} e `Blockly.events.create` + * @return {Array.} List of blocks from this CREATE event. + */ +const adapter = function (e) { + // Validate input + if (typeof e !== 'object') return; + if (typeof e.xml !== 'object') return; + + return domToBlocks(html.parseDOM(e.xml.outerHTML)); +}; + module.exports = adapter; diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 1b562f0d..6d2fa839 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -198,13 +198,14 @@ Blocks.prototype.blocklyListen = function (e, optRuntime) { // Block create/update/destroy switch (e.type) { - case 'create': - var newBlocks = adapter(e); + case 'create': { + const newBlocks = adapter(e); // A create event can create many blocks. Add them all. for (let i = 0; i < newBlocks.length; i++) { this.createBlock(newBlocks[i]); } break; + } case 'change': this.changeBlock({ id: e.blockId, diff --git a/src/engine/execute.js b/src/engine/execute.js index 660eacb3..70785955 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -15,7 +15,7 @@ const isPromise = function (value) { * @param {!Sequencer} sequencer Which sequencer is executing. * @param {!Thread} thread Thread which to read and execute. */ -var execute = function (sequencer, thread) { +const execute = function (sequencer, thread) { const runtime = sequencer.runtime; const target = thread.target; @@ -228,6 +228,8 @@ var execute = function (sequencer, thread) { primitiveReportedValue.then(resolvedValue => { handleReport(resolvedValue); if (typeof resolvedValue === 'undefined') { + let stackFrame; + let nextBlockId; do { // In the case that the promise is the last block in the current thread stack // We need to pop out repeatedly until we find the next block. @@ -235,14 +237,14 @@ var execute = function (sequencer, thread) { if (popped === null) { return; } - var nextBlockId = thread.target.blocks.getNextBlock(popped); + nextBlockId = thread.target.blocks.getNextBlock(popped); if (nextBlockId !== null) { // A next block exists so break out this loop break; } // Investigate the next block and if not in a loop, // then repeat and pop the next item off the stack frame - var stackFrame = thread.peekStackFrame(); + stackFrame = thread.peekStackFrame(); } while (stackFrame !== null && !stackFrame.isLoop); thread.pushStack(nextBlockId); diff --git a/src/engine/mutation-adapter.js b/src/engine/mutation-adapter.js index a057f80f..c40052ff 100644 --- a/src/engine/mutation-adapter.js +++ b/src/engine/mutation-adapter.js @@ -5,7 +5,7 @@ const html = require('htmlparser2'); * @param {object} dom DOM object for mutation tag. * @return {object} Object representing useful parts of this mutation. */ -var mutatorTagToObject = function (dom) { +const mutatorTagToObject = function (dom) { const obj = Object.create(null); obj.tagName = dom.name; obj.children = []; diff --git a/src/import/load-costume.js b/src/import/load-costume.js index 4c4ed9ac..487dc741 100644 --- a/src/import/load-costume.js +++ b/src/import/load-costume.js @@ -48,16 +48,17 @@ const loadCostume = function (md5ext, costume, runtime) { } else { promise = promise.then(costumeAsset => new Promise((resolve, reject) => { const imageElement = new Image(); - let removeEventListeners; // fix no-use-before-define const onError = function () { + // eslint-disable-next-line no-use-before-define removeEventListeners(); reject(); }; const onLoad = function () { + // eslint-disable-next-line no-use-before-define removeEventListeners(); resolve(imageElement); }; - removeEventListeners = function () { + const removeEventListeners = function () { imageElement.removeEventListener('error', onError); imageElement.removeEventListener('load', onLoad); }; diff --git a/src/import/sb2import.js b/src/import/sb2import.js index c896d01d..b78affc5 100644 --- a/src/import/sb2import.js +++ b/src/import/sb2import.js @@ -18,6 +18,115 @@ const List = require('../engine/list'); const loadCostume = require('./load-costume.js'); const loadSound = require('./load-sound.js'); +/** + * 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. + */ +const parseProcedureArgMap = function (procCode) { + const argMap = [ + {} // First item in list is op string. + ]; + const INPUT_PREFIX = 'input'; + let inputCount = 0; + // Split by %n, %b, %s. + const parts = procCode.split(/(?=[^\\]%[nbs])/); + for (let i = 0; i < parts.length; i++) { + const part = parts[i].trim(); + if (part.substring(0, 1) === '%') { + const argType = part.substring(1, 2); + const 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; +}; + +/** + * Flatten a block tree into a block list. + * Children are temporarily stored on the `block.children` property. + * @param {Array.} blocks list generated by `parseBlockList`. + * @return {Array.} Flattened list to be passed to `blocks.createBlock`. + */ +const flatten = function (blocks) { + let finalBlocks = []; + for (let i = 0; i < blocks.length; i++) { + const block = blocks[i]; + finalBlocks.push(block); + if (block.children) { + finalBlocks = finalBlocks.concat(flatten(block.children)); + } + delete block.children; + } + return finalBlocks; +}; + +/** + * Parse any list of blocks from SB2 JSON into a list of VM-format blocks. + * Could be used to parse a top-level script, + * a list of blocks in a branch (e.g., in forever), + * or a list of blocks in an argument (e.g., move [pick random...]). + * @param {Array.} blockList SB2 JSON-format block list. + * @return {Array.} Scratch VM-format block list. + */ +const parseBlockList = function (blockList) { + const resultingList = []; + let previousBlock = null; // For setting next. + for (let i = 0; i < blockList.length; i++) { + const block = blockList[i]; + // eslint-disable-next-line no-use-before-define + const parsedBlock = parseBlock(block); + if (typeof parsedBlock === 'undefined') continue; + if (previousBlock) { + parsedBlock.parent = previousBlock.id; + previousBlock.next = parsedBlock.id; + } + previousBlock = parsedBlock; + resultingList.push(parsedBlock); + } + return resultingList; +}; + +/** + * Parse a Scratch object's scripts into VM blocks. + * This should only handle top-level scripts that include X, Y coordinates. + * @param {!object} scripts Scripts object from SB2 JSON. + * @param {!Blocks} blocks Blocks object to load parsed blocks into. + */ +const parseScripts = function (scripts, blocks) { + for (let i = 0; i < scripts.length; i++) { + const script = scripts[i]; + const scriptX = script[0]; + const scriptY = script[1]; + const blockList = script[2]; + const parsedBlockList = parseBlockList(blockList); + if (parsedBlockList[0]) { + // Adjust script coordinates to account for + // larger block size in scratch-blocks. + // @todo: Determine more precisely the right formulas here. + parsedBlockList[0].x = scriptX * 1.5; + parsedBlockList[0].y = scriptY * 2.2; + parsedBlockList[0].topLevel = true; + parsedBlockList[0].parent = null; + } + // Flatten children and create add the blocks. + const convertedBlocks = flatten(parsedBlockList); + for (let j = 0; j < convertedBlocks.length; j++) { + blocks.createBlock(convertedBlocks[j]); + } + } +}; + /** * Parse a single "Scratch object" and create all its in-memory VM objects. * @param {!object} object From-JSON "Scratch object:" sprite, stage, watcher. @@ -25,7 +134,7 @@ const loadSound = require('./load-sound.js'); * @param {boolean} topLevel Whether this is the top-level object (stage). * @return {?Target} Target created (stage or sprite). */ -var parseScratchObject = function (object, runtime, topLevel) { +const parseScratchObject = function (object, runtime, topLevel) { if (!object.hasOwnProperty('objName')) { // Watcher/monitor - skip this object until those are implemented in VM. // @todo @@ -162,120 +271,12 @@ const sb2import = function (json, runtime, optForceSprite) { ); }; -/** - * Parse a Scratch object's scripts into VM blocks. - * This should only handle top-level scripts that include X, Y coordinates. - * @param {!object} scripts Scripts object from SB2 JSON. - * @param {!Blocks} blocks Blocks object to load parsed blocks into. - */ -var parseScripts = function (scripts, blocks) { - for (let i = 0; i < scripts.length; i++) { - const script = scripts[i]; - const scriptX = script[0]; - const scriptY = script[1]; - const blockList = script[2]; - const parsedBlockList = parseBlockList(blockList); - if (parsedBlockList[0]) { - // Adjust script coordinates to account for - // larger block size in scratch-blocks. - // @todo: Determine more precisely the right formulas here. - parsedBlockList[0].x = scriptX * 1.5; - parsedBlockList[0].y = scriptY * 2.2; - parsedBlockList[0].topLevel = true; - parsedBlockList[0].parent = null; - } - // Flatten children and create add the blocks. - const convertedBlocks = flatten(parsedBlockList); - for (let j = 0; j < convertedBlocks.length; j++) { - blocks.createBlock(convertedBlocks[j]); - } - } -}; - -/** - * Parse any list of blocks from SB2 JSON into a list of VM-format blocks. - * Could be used to parse a top-level script, - * a list of blocks in a branch (e.g., in forever), - * or a list of blocks in an argument (e.g., move [pick random...]). - * @param {Array.} blockList SB2 JSON-format block list. - * @return {Array.} Scratch VM-format block list. - */ -var parseBlockList = function (blockList) { - const resultingList = []; - let previousBlock = null; // For setting next. - for (let i = 0; i < blockList.length; i++) { - const block = blockList[i]; - const parsedBlock = parseBlock(block); - if (typeof parsedBlock === 'undefined') continue; - if (previousBlock) { - parsedBlock.parent = previousBlock.id; - previousBlock.next = parsedBlock.id; - } - previousBlock = parsedBlock; - resultingList.push(parsedBlock); - } - return resultingList; -}; - -/** - * Flatten a block tree into a block list. - * Children are temporarily stored on the `block.children` property. - * @param {Array.} blocks list generated by `parseBlockList`. - * @return {Array.} Flattened list to be passed to `blocks.createBlock`. - */ -var flatten = function (blocks) { - let finalBlocks = []; - for (let i = 0; i < blocks.length; i++) { - const block = blocks[i]; - finalBlocks.push(block); - if (block.children) { - finalBlocks = finalBlocks.concat(flatten(block.children)); - } - delete block.children; - } - 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. - */ -const parseProcedureArgMap = function (procCode) { - const argMap = [ - {} // First item in list is op string. - ]; - const INPUT_PREFIX = 'input'; - let inputCount = 0; - // Split by %n, %b, %s. - const parts = procCode.split(/(?=[^\\]%[nbs])/); - for (let i = 0; i < parts.length; i++) { - const part = parts[i].trim(); - if (part.substring(0, 1) === '%') { - const argType = part.substring(1, 2); - const 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. * @return {object} Scratch VM format block. */ -var parseBlock = function (sb2block) { +const parseBlock = function (sb2block) { // First item in block object is the old opcode (e.g., 'forward:'). const oldOpcode = sb2block[0]; // Convert the block using the specMap. See sb2specmap.js. @@ -317,7 +318,7 @@ var parseBlock = function (sb2block) { }; if (typeof providedArg === 'object' && providedArg) { // Block or block list occupies the input. - var innerBlocks; + let innerBlocks; if (typeof providedArg[0] === 'object' && providedArg[0]) { // Block list occupies the input. innerBlocks = parseBlockList(providedArg); diff --git a/src/playground/playground.js b/src/playground/playground.js index 81d6f746..dbef3606 100644 --- a/src/playground/playground.js +++ b/src/playground/playground.js @@ -120,7 +120,7 @@ window.onload = function () { // Only request data from the VM thread if the appropriate tab is open. Scratch.exploreTabOpen = false; - var getPlaygroundData = function () { + const getPlaygroundData = function () { vm.getPlaygroundData(); if (Scratch.exploreTabOpen) { window.requestAnimationFrame(getPlaygroundData); @@ -268,7 +268,7 @@ window.onload = function () { vm.start(); // Inform VM of animation frames. - var animate = function () { + const animate = function () { stats.update(); requestAnimationFrame(animate); }; diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 845dd329..bc2cc493 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -253,7 +253,9 @@ VirtualMachine.prototype.renameSprite = function (targetId, newName) { throw new Error('No sprite associated with this target.'); } if (newName && RESERVED_NAMES.indexOf(newName) === -1) { - const names = this.runtime.targets.filter(runtimeTarget => runtimeTarget.isSprite()).map(runtimeTarget => runtimeTarget.sprite.name); + const names = this.runtime.targets + .filter(runtimeTarget => runtimeTarget.isSprite()) + .map(runtimeTarget => runtimeTarget.sprite.name); sprite.name = StringUtil.unusedName(newName, names); } From a4f095db5e03e072ba222fe721eeeb543c9b9c15 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 17 Apr 2017 19:42:48 -0400 Subject: [PATCH 293/721] Use ES6 style classes --- src/blocks/scratch3_control.js | 230 ++--- src/blocks/scratch3_data.js | 226 ++--- src/blocks/scratch3_event.js | 154 +-- src/blocks/scratch3_looks.js | 388 ++++---- src/blocks/scratch3_motion.js | 418 ++++---- src/blocks/scratch3_operators.js | 266 ++--- src/blocks/scratch3_pen.js | 603 ++++++------ src/blocks/scratch3_procedures.js | 82 +- src/blocks/scratch3_sensing.js | 328 +++---- src/blocks/scratch3_sound.js | 564 +++++------ src/engine/blocks.js | 888 ++++++++--------- src/engine/list.js | 10 +- src/engine/runtime.js | 1507 +++++++++++++++-------------- src/engine/sequencer.js | 434 +++++---- src/engine/target.js | 252 +++-- src/engine/thread.js | 500 +++++----- src/engine/variable.js | 24 +- src/io/clock.js | 60 +- src/io/keyboard.js | 176 ++-- src/io/mouse.js | 132 +-- src/sprites/rendered-target.js | 1503 ++++++++++++++-------------- src/sprites/sprite.js | 108 ++- src/util/cast.js | 324 ++++--- src/util/clone.js | 22 +- src/util/color.js | 386 ++++---- src/util/math-util.js | 130 +-- src/util/string-util.js | 28 +- src/util/timer.js | 125 +-- src/virtual-machine.js | 768 ++++++++------- 29 files changed, 5375 insertions(+), 5261 deletions(-) diff --git a/src/blocks/scratch3_control.js b/src/blocks/scratch3_control.js index 1d7e8f42..f3b53baa 100644 --- a/src/blocks/scratch3_control.js +++ b/src/blocks/scratch3_control.js @@ -1,139 +1,141 @@ const Cast = require('../util/cast'); const Timer = require('../util/timer'); -const Scratch3ControlBlocks = function (runtime) { +class Scratch3ControlBlocks { + constructor (runtime) { + /** + * The runtime instantiating this block package. + * @type {Runtime} + */ + this.runtime = runtime; + } + /** - * The runtime instantiating this block package. - * @type {Runtime} + * Retrieve the block primitives implemented by this package. + * @return {object.} Mapping of opcode to Function. */ - this.runtime = runtime; -}; + getPrimitives () { + return { + control_repeat: this.repeat, + control_repeat_until: this.repeatUntil, + control_forever: this.forever, + control_wait: this.wait, + control_wait_until: this.waitUntil, + control_if: this.if, + control_if_else: this.ifElse, + control_stop: this.stop, + control_create_clone_of: this.createClone, + control_delete_this_clone: this.deleteClone + }; + } -/** - * Retrieve the block primitives implemented by this package. - * @return {object.} Mapping of opcode to Function. - */ -Scratch3ControlBlocks.prototype.getPrimitives = function () { - return { - control_repeat: this.repeat, - control_repeat_until: this.repeatUntil, - control_forever: this.forever, - control_wait: this.wait, - control_wait_until: this.waitUntil, - control_if: this.if, - control_if_else: this.ifElse, - control_stop: this.stop, - control_create_clone_of: this.createClone, - control_delete_this_clone: this.deleteClone - }; -}; + getHats () { + return { + control_start_as_clone: { + restartExistingThreads: false + } + }; + } -Scratch3ControlBlocks.prototype.getHats = function () { - return { - control_start_as_clone: { - restartExistingThreads: false + repeat (args, util) { + const times = Math.floor(Cast.toNumber(args.TIMES)); + // Initialize loop + if (typeof util.stackFrame.loopCounter === 'undefined') { + util.stackFrame.loopCounter = times; + } + // Only execute once per frame. + // When the branch finishes, `repeat` will be executed again and + // the second branch will be taken, yielding for the rest of the frame. + // Decrease counter + util.stackFrame.loopCounter--; + // If we still have some left, start the branch. + if (util.stackFrame.loopCounter >= 0) { + util.startBranch(1, true); } - }; -}; - -Scratch3ControlBlocks.prototype.repeat = function (args, util) { - const times = Math.floor(Cast.toNumber(args.TIMES)); - // Initialize loop - if (typeof util.stackFrame.loopCounter === 'undefined') { - util.stackFrame.loopCounter = times; } - // Only execute once per frame. - // When the branch finishes, `repeat` will be executed again and - // the second branch will be taken, yielding for the rest of the frame. - // Decrease counter - util.stackFrame.loopCounter--; - // If we still have some left, start the branch. - if (util.stackFrame.loopCounter >= 0) { - util.startBranch(1, true); + + repeatUntil (args, util) { + const condition = Cast.toBoolean(args.CONDITION); + // If the condition is true, start the branch. + if (!condition) { + util.startBranch(1, true); + } } -}; -Scratch3ControlBlocks.prototype.repeatUntil = function (args, util) { - const condition = Cast.toBoolean(args.CONDITION); - // If the condition is true, start the branch. - if (!condition) { - util.startBranch(1, true); - } -}; - -Scratch3ControlBlocks.prototype.waitUntil = function (args, util) { - const condition = Cast.toBoolean(args.CONDITION); - if (!condition) { - util.yield(); - } -}; - -Scratch3ControlBlocks.prototype.forever = function (args, util) { - util.startBranch(1, true); -}; - -Scratch3ControlBlocks.prototype.wait = function (args, util) { - if (!util.stackFrame.timer) { - util.stackFrame.timer = new Timer(); - util.stackFrame.timer.start(); - util.yield(); - this.runtime.requestRedraw(); - } else { - const duration = Math.max(0, 1000 * Cast.toNumber(args.DURATION)); - if (util.stackFrame.timer.timeElapsed() < duration) { + waitUntil (args, util) { + const condition = Cast.toBoolean(args.CONDITION); + if (!condition) { util.yield(); } } -}; -Scratch3ControlBlocks.prototype.if = function (args, util) { - const condition = Cast.toBoolean(args.CONDITION); - if (condition) { - util.startBranch(1, false); + forever (args, util) { + util.startBranch(1, true); } -}; -Scratch3ControlBlocks.prototype.ifElse = function (args, util) { - const condition = Cast.toBoolean(args.CONDITION); - if (condition) { - util.startBranch(1, false); - } else { - util.startBranch(2, false); + wait (args, util) { + if (!util.stackFrame.timer) { + util.stackFrame.timer = new Timer(); + util.stackFrame.timer.start(); + util.yield(); + this.runtime.requestRedraw(); + } else { + const duration = Math.max(0, 1000 * Cast.toNumber(args.DURATION)); + if (util.stackFrame.timer.timeElapsed() < duration) { + util.yield(); + } + } } -}; -Scratch3ControlBlocks.prototype.stop = function (args, util) { - const 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.stopThisScript(); + if (args, util) { + const condition = Cast.toBoolean(args.CONDITION); + if (condition) { + util.startBranch(1, false); + } } -}; -Scratch3ControlBlocks.prototype.createClone = function (args, util) { - let cloneTarget; - if (args.CLONE_OPTION === '_myself_') { - cloneTarget = util.target; - } else { - cloneTarget = this.runtime.getSpriteTargetByName(args.CLONE_OPTION); + ifElse (args, util) { + const condition = Cast.toBoolean(args.CONDITION); + if (condition) { + util.startBranch(1, false); + } else { + util.startBranch(2, false); + } } - if (!cloneTarget) { - return; - } - const newClone = cloneTarget.makeClone(); - if (newClone) { - this.runtime.targets.push(newClone); - } -}; -Scratch3ControlBlocks.prototype.deleteClone = function (args, util) { - if (util.target.isOriginal) return; - this.runtime.disposeTarget(util.target); - this.runtime.stopForTarget(util.target); -}; + stop (args, util) { + const 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.stopThisScript(); + } + } + + createClone (args, util) { + let cloneTarget; + if (args.CLONE_OPTION === '_myself_') { + cloneTarget = util.target; + } else { + cloneTarget = this.runtime.getSpriteTargetByName(args.CLONE_OPTION); + } + if (!cloneTarget) { + return; + } + const newClone = cloneTarget.makeClone(); + if (newClone) { + this.runtime.targets.push(newClone); + } + } + + deleteClone (args, util) { + if (util.target.isOriginal) return; + this.runtime.disposeTarget(util.target); + this.runtime.stopForTarget(util.target); + } +} module.exports = Scratch3ControlBlocks; diff --git a/src/blocks/scratch3_data.js b/src/blocks/scratch3_data.js index 3b3b0613..551f717f 100644 --- a/src/blocks/scratch3_data.js +++ b/src/blocks/scratch3_data.js @@ -1,136 +1,138 @@ const Cast = require('../util/cast'); -const Scratch3DataBlocks = function (runtime) { +class Scratch3DataBlocks { + constructor (runtime) { + /** + * The runtime instantiating this block package. + * @type {Runtime} + */ + this.runtime = runtime; + } + /** - * The runtime instantiating this block package. - * @type {Runtime} + * Retrieve the block primitives implemented by this package. + * @return {object.} Mapping of opcode to Function. */ - this.runtime = runtime; -}; + getPrimitives () { + return { + data_variable: this.getVariable, + data_setvariableto: this.setVariableTo, + data_changevariableby: this.changeVariableBy, + data_listcontents: this.getListContents, + data_addtolist: this.addToList, + data_deleteoflist: this.deleteOfList, + data_insertatlist: this.insertAtList, + data_replaceitemoflist: this.replaceItemOfList, + data_itemoflist: this.getItemOfList, + data_lengthoflist: this.lengthOfList, + data_listcontainsitem: this.listContainsItem + }; + } -/** - * Retrieve the block primitives implemented by this package. - * @return {object.} Mapping of opcode to Function. - */ -Scratch3DataBlocks.prototype.getPrimitives = function () { - return { - data_variable: this.getVariable, - data_setvariableto: this.setVariableTo, - data_changevariableby: this.changeVariableBy, - data_listcontents: this.getListContents, - data_addtolist: this.addToList, - data_deleteoflist: this.deleteOfList, - data_insertatlist: this.insertAtList, - data_replaceitemoflist: this.replaceItemOfList, - data_itemoflist: this.getItemOfList, - data_lengthoflist: this.lengthOfList, - data_listcontainsitem: this.listContainsItem - }; -}; + getVariable (args, util) { + const variable = util.target.lookupOrCreateVariable(args.VARIABLE); + return variable.value; + } -Scratch3DataBlocks.prototype.getVariable = function (args, util) { - const variable = util.target.lookupOrCreateVariable(args.VARIABLE); - return variable.value; -}; + setVariableTo (args, util) { + const variable = util.target.lookupOrCreateVariable(args.VARIABLE); + variable.value = args.VALUE; + } -Scratch3DataBlocks.prototype.setVariableTo = function (args, util) { - const variable = util.target.lookupOrCreateVariable(args.VARIABLE); - variable.value = args.VALUE; -}; + changeVariableBy (args, util) { + const variable = util.target.lookupOrCreateVariable(args.VARIABLE); + const castedValue = Cast.toNumber(variable.value); + const dValue = Cast.toNumber(args.VALUE); + variable.value = castedValue + dValue; + } -Scratch3DataBlocks.prototype.changeVariableBy = function (args, util) { - const variable = util.target.lookupOrCreateVariable(args.VARIABLE); - const castedValue = Cast.toNumber(variable.value); - const dValue = Cast.toNumber(args.VALUE); - variable.value = castedValue + dValue; -}; - -Scratch3DataBlocks.prototype.getListContents = function (args, util) { - const list = util.target.lookupOrCreateList(args.LIST); - // Determine if the list is all single letters. - // If it is, report contents joined together with no separator. - // If it's not, report contents joined together with a space. - let allSingleLetters = true; - for (let i = 0; i < list.contents.length; i++) { - const listItem = list.contents[i]; - if (!((typeof listItem === 'string') && - (listItem.length === 1))) { - allSingleLetters = false; - break; + getListContents (args, util) { + const list = util.target.lookupOrCreateList(args.LIST); + // Determine if the list is all single letters. + // If it is, report contents joined together with no separator. + // If it's not, report contents joined together with a space. + let allSingleLetters = true; + for (let i = 0; i < list.contents.length; i++) { + const listItem = list.contents[i]; + if (!((typeof listItem === 'string') && + (listItem.length === 1))) { + allSingleLetters = false; + break; + } } - } - if (allSingleLetters) { - return list.contents.join(''); - } - return list.contents.join(' '); - -}; + if (allSingleLetters) { + return list.contents.join(''); + } + return list.contents.join(' '); -Scratch3DataBlocks.prototype.addToList = function (args, util) { - const list = util.target.lookupOrCreateList(args.LIST); - list.contents.push(args.ITEM); -}; - -Scratch3DataBlocks.prototype.deleteOfList = function (args, util) { - const list = util.target.lookupOrCreateList(args.LIST); - const index = Cast.toListIndex(args.INDEX, list.contents.length); - if (index === Cast.LIST_INVALID) { - return; - } else if (index === Cast.LIST_ALL) { - list.contents = []; - return; } - list.contents.splice(index - 1, 1); -}; -Scratch3DataBlocks.prototype.insertAtList = function (args, util) { - const item = args.ITEM; - const list = util.target.lookupOrCreateList(args.LIST); - const index = Cast.toListIndex(args.INDEX, list.contents.length + 1); - if (index === Cast.LIST_INVALID) { - return; + addToList (args, util) { + const list = util.target.lookupOrCreateList(args.LIST); + list.contents.push(args.ITEM); } - list.contents.splice(index - 1, 0, item); -}; -Scratch3DataBlocks.prototype.replaceItemOfList = function (args, util) { - const item = args.ITEM; - const list = util.target.lookupOrCreateList(args.LIST); - const index = Cast.toListIndex(args.INDEX, list.contents.length); - if (index === Cast.LIST_INVALID) { - return; + deleteOfList (args, util) { + const list = util.target.lookupOrCreateList(args.LIST); + const index = Cast.toListIndex(args.INDEX, list.contents.length); + if (index === Cast.LIST_INVALID) { + return; + } else if (index === Cast.LIST_ALL) { + list.contents = []; + return; + } + list.contents.splice(index - 1, 1); } - list.contents.splice(index - 1, 1, item); -}; -Scratch3DataBlocks.prototype.getItemOfList = function (args, util) { - const list = util.target.lookupOrCreateList(args.LIST); - const index = Cast.toListIndex(args.INDEX, list.contents.length); - if (index === Cast.LIST_INVALID) { - return ''; + insertAtList (args, util) { + const item = args.ITEM; + const list = util.target.lookupOrCreateList(args.LIST); + const index = Cast.toListIndex(args.INDEX, list.contents.length + 1); + if (index === Cast.LIST_INVALID) { + return; + } + list.contents.splice(index - 1, 0, item); } - return list.contents[index - 1]; -}; -Scratch3DataBlocks.prototype.lengthOfList = function (args, util) { - const list = util.target.lookupOrCreateList(args.LIST); - return list.contents.length; -}; - -Scratch3DataBlocks.prototype.listContainsItem = function (args, util) { - const item = args.ITEM; - const list = util.target.lookupOrCreateList(args.LIST); - if (list.contents.indexOf(item) >= 0) { - return true; + replaceItemOfList (args, util) { + const item = args.ITEM; + const list = util.target.lookupOrCreateList(args.LIST); + const index = Cast.toListIndex(args.INDEX, list.contents.length); + if (index === Cast.LIST_INVALID) { + return; + } + list.contents.splice(index - 1, 1, item); } - // Try using Scratch comparison operator on each item. - // (Scratch considers the string '123' equal to the number 123). - for (let i = 0; i < list.contents.length; i++) { - if (Cast.compare(list.contents[i], item) === 0) { + + getItemOfList (args, util) { + const list = util.target.lookupOrCreateList(args.LIST); + const index = Cast.toListIndex(args.INDEX, list.contents.length); + if (index === Cast.LIST_INVALID) { + return ''; + } + return list.contents[index - 1]; + } + + lengthOfList (args, util) { + const list = util.target.lookupOrCreateList(args.LIST); + return list.contents.length; + } + + listContainsItem (args, util) { + const item = args.ITEM; + const list = util.target.lookupOrCreateList(args.LIST); + if (list.contents.indexOf(item) >= 0) { return true; } + // Try using Scratch comparison operator on each item. + // (Scratch considers the string '123' equal to the number 123). + for (let i = 0; i < list.contents.length; i++) { + if (Cast.compare(list.contents[i], item) === 0) { + return true; + } + } + return false; } - return false; -}; +} module.exports = Scratch3DataBlocks; diff --git a/src/blocks/scratch3_event.js b/src/blocks/scratch3_event.js index 62d65fa1..8687141c 100644 --- a/src/blocks/scratch3_event.js +++ b/src/blocks/scratch3_event.js @@ -1,87 +1,89 @@ const Cast = require('../util/cast'); -const Scratch3EventBlocks = function (runtime) { +class Scratch3EventBlocks { + constructor (runtime) { + /** + * The runtime instantiating this block package. + * @type {Runtime} + */ + this.runtime = runtime; + } + /** - * The runtime instantiating this block package. - * @type {Runtime} + * Retrieve the block primitives implemented by this package. + * @return {object.} Mapping of opcode to Function. */ - this.runtime = runtime; -}; - -/** - * Retrieve the block primitives implemented by this package. - * @return {object.} Mapping of opcode to Function. - */ -Scratch3EventBlocks.prototype.getPrimitives = function () { - return { - event_broadcast: this.broadcast, - event_broadcastandwait: this.broadcastAndWait, - event_whengreaterthan: this.hatGreaterThanPredicate - }; -}; - -Scratch3EventBlocks.prototype.getHats = function () { - return { - event_whenflagclicked: { - restartExistingThreads: true - }, - event_whenkeypressed: { - restartExistingThreads: false - }, - event_whenthisspriteclicked: { - restartExistingThreads: true - }, - event_whenbackdropswitchesto: { - restartExistingThreads: true - }, - event_whengreaterthan: { - restartExistingThreads: false, - edgeActivated: true - }, - event_whenbroadcastreceived: { - restartExistingThreads: true - } - }; -}; - -Scratch3EventBlocks.prototype.hatGreaterThanPredicate = function (args, util) { - const option = Cast.toString(args.WHENGREATERTHANMENU).toLowerCase(); - const value = Cast.toNumber(args.VALUE); - // @todo: Other cases :) - if (option === 'timer') { - return util.ioQuery('clock', 'projectTimer') > value; + getPrimitives () { + return { + event_broadcast: this.broadcast, + event_broadcastandwait: this.broadcastAndWait, + event_whengreaterthan: this.hatGreaterThanPredicate + }; } - return false; -}; -Scratch3EventBlocks.prototype.broadcast = function (args, util) { - const broadcastOption = Cast.toString(args.BROADCAST_OPTION); - util.startHats('event_whenbroadcastreceived', { - BROADCAST_OPTION: broadcastOption - }); -}; - -Scratch3EventBlocks.prototype.broadcastAndWait = function (args, util) { - const broadcastOption = Cast.toString(args.BROADCAST_OPTION); - // Have we run before, starting threads? - if (!util.stackFrame.startedThreads) { - // No - start hats for this broadcast. - util.stackFrame.startedThreads = util.startHats( - 'event_whenbroadcastreceived', { - BROADCAST_OPTION: broadcastOption + getHats () { + return { + event_whenflagclicked: { + restartExistingThreads: true + }, + event_whenkeypressed: { + restartExistingThreads: false + }, + event_whenthisspriteclicked: { + restartExistingThreads: true + }, + event_whenbackdropswitchesto: { + restartExistingThreads: true + }, + event_whengreaterthan: { + restartExistingThreads: false, + edgeActivated: true + }, + event_whenbroadcastreceived: { + restartExistingThreads: true } - ); - if (util.stackFrame.startedThreads.length === 0) { - // Nothing was started. - return; + }; + } + + hatGreaterThanPredicate (args, util) { + const option = Cast.toString(args.WHENGREATERTHANMENU).toLowerCase(); + const value = Cast.toNumber(args.VALUE); + // @todo: Other cases :) + if (option === 'timer') { + return util.ioQuery('clock', 'projectTimer') > value; + } + return false; + } + + broadcast (args, util) { + const broadcastOption = Cast.toString(args.BROADCAST_OPTION); + util.startHats('event_whenbroadcastreceived', { + BROADCAST_OPTION: broadcastOption + }); + } + + broadcastAndWait (args, util) { + const broadcastOption = Cast.toString(args.BROADCAST_OPTION); + // Have we run before, starting threads? + if (!util.stackFrame.startedThreads) { + // No - start hats for this broadcast. + util.stackFrame.startedThreads = util.startHats( + 'event_whenbroadcastreceived', { + BROADCAST_OPTION: broadcastOption + } + ); + if (util.stackFrame.startedThreads.length === 0) { + // Nothing was started. + return; + } + } + // We've run before; check if the wait is still going on. + const instance = this; + const waiting = util.stackFrame.startedThreads.some(thread => instance.runtime.isActiveThread(thread)); + if (waiting) { + util.yield(); } } - // We've run before; check if the wait is still going on. - const instance = this; - const waiting = util.stackFrame.startedThreads.some(thread => instance.runtime.isActiveThread(thread)); - if (waiting) { - util.yield(); - } -}; +} module.exports = Scratch3EventBlocks; diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index e0807337..0caa04ff 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -1,219 +1,221 @@ const Cast = require('../util/cast'); -const Scratch3LooksBlocks = function (runtime) { - /** - * The runtime instantiating this block package. - * @type {Runtime} - */ - this.runtime = runtime; -}; - -/** - * Retrieve the block primitives implemented by this package. - * @return {object.} Mapping of opcode to Function. - */ -Scratch3LooksBlocks.prototype.getPrimitives = function () { - return { - looks_say: this.say, - looks_sayforsecs: this.sayforsecs, - looks_think: this.think, - looks_thinkforsecs: this.sayforsecs, - looks_show: this.show, - looks_hide: this.hide, - looks_switchcostumeto: this.switchCostume, - looks_switchbackdropto: this.switchBackdrop, - looks_switchbackdroptoandwait: this.switchBackdropAndWait, - looks_nextcostume: this.nextCostume, - looks_nextbackdrop: this.nextBackdrop, - looks_changeeffectby: this.changeEffect, - looks_seteffectto: this.setEffect, - looks_cleargraphiceffects: this.clearEffects, - looks_changesizeby: this.changeSize, - looks_setsizeto: this.setSize, - looks_gotofront: this.goToFront, - looks_gobacklayers: this.goBackLayers, - looks_size: this.getSize, - looks_costumeorder: this.getCostumeIndex, - looks_backdroporder: this.getBackdropIndex, - looks_backdropname: this.getBackdropName - }; -}; - -Scratch3LooksBlocks.prototype.say = function (args, util) { - util.target.setSay('say', args.MESSAGE); -}; - -Scratch3LooksBlocks.prototype.sayforsecs = function (args, util) { - util.target.setSay('say', args.MESSAGE); - return new Promise(resolve => { - setTimeout(() => { - // Clear say bubble and proceed. - util.target.setSay(); - resolve(); - }, 1000 * args.SECS); - }); -}; - -Scratch3LooksBlocks.prototype.think = function (args, util) { - util.target.setSay('think', args.MESSAGE); -}; - -Scratch3LooksBlocks.prototype.thinkforsecs = function (args, util) { - util.target.setSay('think', args.MESSAGE); - return new Promise(resolve => { - setTimeout(() => { - // Clear say bubble and proceed. - util.target.setSay(); - resolve(); - }, 1000 * args.SECS); - }); -}; - -Scratch3LooksBlocks.prototype.show = function (args, util) { - util.target.setVisible(true); -}; - -Scratch3LooksBlocks.prototype.hide = function (args, util) { - util.target.setVisible(false); -}; - -/** - * Utility function to set the costume or backdrop of a target. - * Matches the behavior of Scratch 2.0 for different types of arguments. - * @param {!Target} target Target to set costume/backdrop to. - * @param {Any} requestedCostume Costume requested, e.g., 0, 'name', etc. - * @param {boolean=} optZeroIndex Set to zero-index the requestedCostume. - * @return {Array.} Any threads started by this switch. - */ -Scratch3LooksBlocks.prototype._setCostumeOrBackdrop = function (target, - requestedCostume, optZeroIndex) { - if (typeof requestedCostume === 'number') { - target.setCostume(optZeroIndex ? - requestedCostume : requestedCostume - 1); - } else { - const costumeIndex = target.getCostumeIndexByName(requestedCostume); - if (costumeIndex > -1) { - target.setCostume(costumeIndex); - } else if (requestedCostume === 'previous costume' || - requestedCostume === 'previous backdrop') { - target.setCostume(target.currentCostume - 1); - } else if (requestedCostume === 'next costume' || - requestedCostume === 'next backdrop') { - target.setCostume(target.currentCostume + 1); - } else { - const forcedNumber = Number(requestedCostume); - if (!isNaN(forcedNumber)) { - target.setCostume(optZeroIndex ? - forcedNumber : forcedNumber - 1); - } - } +class Scratch3LooksBlocks { + constructor (runtime) { + /** + * The runtime instantiating this block package. + * @type {Runtime} + */ + this.runtime = runtime; } - if (target === this.runtime.getTargetForStage()) { - // Target is the stage - start hats. - const newName = target.sprite.costumes[target.currentCostume].name; - return this.runtime.startHats('event_whenbackdropswitchesto', { - BACKDROP: newName + + /** + * Retrieve the block primitives implemented by this package. + * @return {object.} Mapping of opcode to Function. + */ + getPrimitives () { + return { + looks_say: this.say, + looks_sayforsecs: this.sayforsecs, + looks_think: this.think, + looks_thinkforsecs: this.sayforsecs, + looks_show: this.show, + looks_hide: this.hide, + looks_switchcostumeto: this.switchCostume, + looks_switchbackdropto: this.switchBackdrop, + looks_switchbackdroptoandwait: this.switchBackdropAndWait, + looks_nextcostume: this.nextCostume, + looks_nextbackdrop: this.nextBackdrop, + looks_changeeffectby: this.changeEffect, + looks_seteffectto: this.setEffect, + looks_cleargraphiceffects: this.clearEffects, + looks_changesizeby: this.changeSize, + looks_setsizeto: this.setSize, + looks_gotofront: this.goToFront, + looks_gobacklayers: this.goBackLayers, + looks_size: this.getSize, + looks_costumeorder: this.getCostumeIndex, + looks_backdroporder: this.getBackdropIndex, + looks_backdropname: this.getBackdropName + }; + } + + say (args, util) { + util.target.setSay('say', args.MESSAGE); + } + + sayforsecs (args, util) { + util.target.setSay('say', args.MESSAGE); + return new Promise(resolve => { + setTimeout(() => { + // Clear say bubble and proceed. + util.target.setSay(); + resolve(); + }, 1000 * args.SECS); }); } - return []; -}; -Scratch3LooksBlocks.prototype.switchCostume = function (args, util) { - this._setCostumeOrBackdrop(util.target, args.COSTUME); -}; + think (args, util) { + util.target.setSay('think', args.MESSAGE); + } -Scratch3LooksBlocks.prototype.nextCostume = function (args, util) { - this._setCostumeOrBackdrop( - util.target, util.target.currentCostume + 1, true - ); -}; + thinkforsecs (args, util) { + util.target.setSay('think', args.MESSAGE); + return new Promise(resolve => { + setTimeout(() => { + // Clear say bubble and proceed. + util.target.setSay(); + resolve(); + }, 1000 * args.SECS); + }); + } -Scratch3LooksBlocks.prototype.switchBackdrop = function (args) { - this._setCostumeOrBackdrop(this.runtime.getTargetForStage(), args.BACKDROP); -}; + show (args, util) { + util.target.setVisible(true); + } -Scratch3LooksBlocks.prototype.switchBackdropAndWait = function (args, util) { - // Have we run before, starting threads? - if (!util.stackFrame.startedThreads) { - // No - switch the backdrop. - util.stackFrame.startedThreads = ( - this._setCostumeOrBackdrop( - this.runtime.getTargetForStage(), - args.BACKDROP - ) + hide (args, util) { + util.target.setVisible(false); + } + + /** + * Utility function to set the costume or backdrop of a target. + * Matches the behavior of Scratch 2.0 for different types of arguments. + * @param {!Target} target Target to set costume/backdrop to. + * @param {Any} requestedCostume Costume requested, e.g., 0, 'name', etc. + * @param {boolean=} optZeroIndex Set to zero-index the requestedCostume. + * @return {Array.} Any threads started by this switch. + */ + _setCostumeOrBackdrop (target, + requestedCostume, optZeroIndex) { + if (typeof requestedCostume === 'number') { + target.setCostume(optZeroIndex ? + requestedCostume : requestedCostume - 1); + } else { + const costumeIndex = target.getCostumeIndexByName(requestedCostume); + if (costumeIndex > -1) { + target.setCostume(costumeIndex); + } else if (requestedCostume === 'previous costume' || + requestedCostume === 'previous backdrop') { + target.setCostume(target.currentCostume - 1); + } else if (requestedCostume === 'next costume' || + requestedCostume === 'next backdrop') { + target.setCostume(target.currentCostume + 1); + } else { + const forcedNumber = Number(requestedCostume); + if (!isNaN(forcedNumber)) { + target.setCostume(optZeroIndex ? + forcedNumber : forcedNumber - 1); + } + } + } + if (target === this.runtime.getTargetForStage()) { + // Target is the stage - start hats. + const newName = target.sprite.costumes[target.currentCostume].name; + return this.runtime.startHats('event_whenbackdropswitchesto', { + BACKDROP: newName + }); + } + return []; + } + + switchCostume (args, util) { + this._setCostumeOrBackdrop(util.target, args.COSTUME); + } + + nextCostume (args, util) { + this._setCostumeOrBackdrop( + util.target, util.target.currentCostume + 1, true ); - if (util.stackFrame.startedThreads.length === 0) { - // Nothing was started. - return; + } + + switchBackdrop (args) { + this._setCostumeOrBackdrop(this.runtime.getTargetForStage(), args.BACKDROP); + } + + switchBackdropAndWait (args, util) { + // Have we run before, starting threads? + if (!util.stackFrame.startedThreads) { + // No - switch the backdrop. + util.stackFrame.startedThreads = ( + this._setCostumeOrBackdrop( + this.runtime.getTargetForStage(), + args.BACKDROP + ) + ); + if (util.stackFrame.startedThreads.length === 0) { + // Nothing was started. + return; + } + } + // We've run before; check if the wait is still going on. + const instance = this; + const waiting = util.stackFrame.startedThreads.some(thread => instance.runtime.isActiveThread(thread)); + if (waiting) { + util.yield(); } } - // We've run before; check if the wait is still going on. - const instance = this; - const waiting = util.stackFrame.startedThreads.some(thread => instance.runtime.isActiveThread(thread)); - if (waiting) { - util.yield(); + + nextBackdrop () { + const stage = this.runtime.getTargetForStage(); + this._setCostumeOrBackdrop( + stage, stage.currentCostume + 1, true + ); } -}; -Scratch3LooksBlocks.prototype.nextBackdrop = function () { - const stage = this.runtime.getTargetForStage(); - this._setCostumeOrBackdrop( - stage, stage.currentCostume + 1, true - ); -}; + changeEffect (args, util) { + const effect = Cast.toString(args.EFFECT).toLowerCase(); + const change = Cast.toNumber(args.CHANGE); + if (!util.target.effects.hasOwnProperty(effect)) return; + const newValue = change + util.target.effects[effect]; + util.target.setEffect(effect, newValue); + } -Scratch3LooksBlocks.prototype.changeEffect = function (args, util) { - const effect = Cast.toString(args.EFFECT).toLowerCase(); - const change = Cast.toNumber(args.CHANGE); - if (!util.target.effects.hasOwnProperty(effect)) return; - const newValue = change + util.target.effects[effect]; - util.target.setEffect(effect, newValue); -}; + setEffect (args, util) { + const effect = Cast.toString(args.EFFECT).toLowerCase(); + const value = Cast.toNumber(args.VALUE); + util.target.setEffect(effect, value); + } -Scratch3LooksBlocks.prototype.setEffect = function (args, util) { - const effect = Cast.toString(args.EFFECT).toLowerCase(); - const value = Cast.toNumber(args.VALUE); - util.target.setEffect(effect, value); -}; + clearEffects (args, util) { + util.target.clearEffects(); + } -Scratch3LooksBlocks.prototype.clearEffects = function (args, util) { - util.target.clearEffects(); -}; + changeSize (args, util) { + const change = Cast.toNumber(args.CHANGE); + util.target.setSize(util.target.size + change); + } -Scratch3LooksBlocks.prototype.changeSize = function (args, util) { - const change = Cast.toNumber(args.CHANGE); - util.target.setSize(util.target.size + change); -}; + setSize (args, util) { + const size = Cast.toNumber(args.SIZE); + util.target.setSize(size); + } -Scratch3LooksBlocks.prototype.setSize = function (args, util) { - const size = Cast.toNumber(args.SIZE); - util.target.setSize(size); -}; + goToFront (args, util) { + util.target.goToFront(); + } -Scratch3LooksBlocks.prototype.goToFront = function (args, util) { - util.target.goToFront(); -}; + goBackLayers (args, util) { + util.target.goBackLayers(args.NUM); + } -Scratch3LooksBlocks.prototype.goBackLayers = function (args, util) { - util.target.goBackLayers(args.NUM); -}; + getSize (args, util) { + return Math.round(util.target.size); + } -Scratch3LooksBlocks.prototype.getSize = function (args, util) { - return Math.round(util.target.size); -}; + getBackdropIndex () { + const stage = this.runtime.getTargetForStage(); + return stage.currentCostume + 1; + } -Scratch3LooksBlocks.prototype.getBackdropIndex = function () { - const stage = this.runtime.getTargetForStage(); - return stage.currentCostume + 1; -}; + getBackdropName () { + const stage = this.runtime.getTargetForStage(); + return stage.sprite.costumes[stage.currentCostume].name; + } -Scratch3LooksBlocks.prototype.getBackdropName = function () { - const stage = this.runtime.getTargetForStage(); - return stage.sprite.costumes[stage.currentCostume].name; -}; - -Scratch3LooksBlocks.prototype.getCostumeIndex = function (args, util) { - return util.target.currentCostume + 1; -}; + getCostumeIndex (args, util) { + return util.target.currentCostume + 1; + } +} module.exports = Scratch3LooksBlocks; diff --git a/src/blocks/scratch3_motion.js b/src/blocks/scratch3_motion.js index c70eb3bd..21caa4d6 100644 --- a/src/blocks/scratch3_motion.js +++ b/src/blocks/scratch3_motion.js @@ -2,233 +2,235 @@ const Cast = require('../util/cast'); const MathUtil = require('../util/math-util'); const Timer = require('../util/timer'); -const Scratch3MotionBlocks = function (runtime) { +class Scratch3MotionBlocks { + constructor (runtime) { + /** + * The runtime instantiating this block package. + * @type {Runtime} + */ + this.runtime = runtime; + } + /** - * The runtime instantiating this block package. - * @type {Runtime} + * Retrieve the block primitives implemented by this package. + * @return {object.} Mapping of opcode to Function. */ - this.runtime = runtime; -}; - -/** - * Retrieve the block primitives implemented by this package. - * @return {object.} Mapping of opcode to Function. - */ -Scratch3MotionBlocks.prototype.getPrimitives = function () { - return { - motion_movesteps: this.moveSteps, - motion_gotoxy: this.goToXY, - motion_goto: this.goTo, - motion_turnright: this.turnRight, - motion_turnleft: this.turnLeft, - motion_pointindirection: this.pointInDirection, - motion_pointtowards: this.pointTowards, - motion_glidesecstoxy: this.glide, - motion_ifonedgebounce: this.ifOnEdgeBounce, - motion_setrotationstyle: this.setRotationStyle, - motion_changexby: this.changeX, - motion_setx: this.setX, - motion_changeyby: this.changeY, - motion_sety: this.setY, - motion_xposition: this.getX, - motion_yposition: this.getY, - motion_direction: this.getDirection - }; -}; - -Scratch3MotionBlocks.prototype.moveSteps = function (args, util) { - const steps = Cast.toNumber(args.STEPS); - const radians = MathUtil.degToRad(90 - util.target.direction); - const dx = steps * Math.cos(radians); - const dy = steps * Math.sin(radians); - util.target.setXY(util.target.x + dx, util.target.y + dy); -}; - -Scratch3MotionBlocks.prototype.goToXY = function (args, util) { - const x = Cast.toNumber(args.X); - const y = Cast.toNumber(args.Y); - util.target.setXY(x, y); -}; - -Scratch3MotionBlocks.prototype.goTo = function (args, util) { - let targetX = 0; - let targetY = 0; - if (args.TO === '_mouse_') { - targetX = util.ioQuery('mouse', 'getX'); - targetY = util.ioQuery('mouse', 'getY'); - } else if (args.TO === '_random_') { - const stageWidth = this.runtime.constructor.STAGE_WIDTH; - const stageHeight = this.runtime.constructor.STAGE_HEIGHT; - targetX = Math.round(stageWidth * (Math.random() - 0.5)); - targetY = Math.round(stageHeight * (Math.random() - 0.5)); - } else { - const goToTarget = this.runtime.getSpriteTargetByName(args.TO); - if (!goToTarget) return; - targetX = goToTarget.x; - targetY = goToTarget.y; - } - util.target.setXY(targetX, targetY); -}; - -Scratch3MotionBlocks.prototype.turnRight = function (args, util) { - const degrees = Cast.toNumber(args.DEGREES); - util.target.setDirection(util.target.direction + degrees); -}; - -Scratch3MotionBlocks.prototype.turnLeft = function (args, util) { - const degrees = Cast.toNumber(args.DEGREES); - util.target.setDirection(util.target.direction - degrees); -}; - -Scratch3MotionBlocks.prototype.pointInDirection = function (args, util) { - const direction = Cast.toNumber(args.DIRECTION); - util.target.setDirection(direction); -}; - -Scratch3MotionBlocks.prototype.pointTowards = function (args, util) { - let targetX = 0; - let targetY = 0; - if (args.TOWARDS === '_mouse_') { - targetX = util.ioQuery('mouse', 'getX'); - targetY = util.ioQuery('mouse', 'getY'); - } else { - const pointTarget = this.runtime.getSpriteTargetByName(args.TOWARDS); - if (!pointTarget) return; - targetX = pointTarget.x; - targetY = pointTarget.y; + getPrimitives () { + return { + motion_movesteps: this.moveSteps, + motion_gotoxy: this.goToXY, + motion_goto: this.goTo, + motion_turnright: this.turnRight, + motion_turnleft: this.turnLeft, + motion_pointindirection: this.pointInDirection, + motion_pointtowards: this.pointTowards, + motion_glidesecstoxy: this.glide, + motion_ifonedgebounce: this.ifOnEdgeBounce, + motion_setrotationstyle: this.setRotationStyle, + motion_changexby: this.changeX, + motion_setx: this.setX, + motion_changeyby: this.changeY, + motion_sety: this.setY, + motion_xposition: this.getX, + motion_yposition: this.getY, + motion_direction: this.getDirection + }; } - const dx = targetX - util.target.x; - const dy = targetY - util.target.y; - const direction = 90 - MathUtil.radToDeg(Math.atan2(dy, dx)); - util.target.setDirection(direction); -}; + moveSteps (args, util) { + const steps = Cast.toNumber(args.STEPS); + const radians = MathUtil.degToRad(90 - util.target.direction); + const dx = steps * Math.cos(radians); + const dy = steps * Math.sin(radians); + util.target.setXY(util.target.x + dx, util.target.y + dy); + } -Scratch3MotionBlocks.prototype.glide = function (args, util) { - if (!util.stackFrame.timer) { - // First time: save data for future use. - util.stackFrame.timer = new Timer(); - util.stackFrame.timer.start(); - util.stackFrame.duration = Cast.toNumber(args.SECS); - util.stackFrame.startX = util.target.x; - util.stackFrame.startY = util.target.y; - util.stackFrame.endX = Cast.toNumber(args.X); - util.stackFrame.endY = Cast.toNumber(args.Y); - if (util.stackFrame.duration <= 0) { - // Duration too short to glide. - util.target.setXY(util.stackFrame.endX, util.stackFrame.endY); - return; + goToXY (args, util) { + const x = Cast.toNumber(args.X); + const y = Cast.toNumber(args.Y); + util.target.setXY(x, y); + } + + goTo (args, util) { + let targetX = 0; + let targetY = 0; + if (args.TO === '_mouse_') { + targetX = util.ioQuery('mouse', 'getX'); + targetY = util.ioQuery('mouse', 'getY'); + } else if (args.TO === '_random_') { + const stageWidth = this.runtime.constructor.STAGE_WIDTH; + const stageHeight = this.runtime.constructor.STAGE_HEIGHT; + targetX = Math.round(stageWidth * (Math.random() - 0.5)); + targetY = Math.round(stageHeight * (Math.random() - 0.5)); + } else { + const goToTarget = this.runtime.getSpriteTargetByName(args.TO); + if (!goToTarget) return; + targetX = goToTarget.x; + targetY = goToTarget.y; } - util.yield(); - } else { - const timeElapsed = util.stackFrame.timer.timeElapsed(); - if (timeElapsed < util.stackFrame.duration * 1000) { - // In progress: move to intermediate position. - const frac = timeElapsed / (util.stackFrame.duration * 1000); - const dx = frac * (util.stackFrame.endX - util.stackFrame.startX); - const dy = frac * (util.stackFrame.endY - util.stackFrame.startY); - util.target.setXY( - util.stackFrame.startX + dx, - util.stackFrame.startY + dy - ); + util.target.setXY(targetX, targetY); + } + + turnRight (args, util) { + const degrees = Cast.toNumber(args.DEGREES); + util.target.setDirection(util.target.direction + degrees); + } + + turnLeft (args, util) { + const degrees = Cast.toNumber(args.DEGREES); + util.target.setDirection(util.target.direction - degrees); + } + + pointInDirection (args, util) { + const direction = Cast.toNumber(args.DIRECTION); + util.target.setDirection(direction); + } + + pointTowards (args, util) { + let targetX = 0; + let targetY = 0; + if (args.TOWARDS === '_mouse_') { + targetX = util.ioQuery('mouse', 'getX'); + targetY = util.ioQuery('mouse', 'getY'); + } else { + const pointTarget = this.runtime.getSpriteTargetByName(args.TOWARDS); + if (!pointTarget) return; + targetX = pointTarget.x; + targetY = pointTarget.y; + } + + const dx = targetX - util.target.x; + const dy = targetY - util.target.y; + const direction = 90 - MathUtil.radToDeg(Math.atan2(dy, dx)); + util.target.setDirection(direction); + } + + glide (args, util) { + if (!util.stackFrame.timer) { + // First time: save data for future use. + util.stackFrame.timer = new Timer(); + util.stackFrame.timer.start(); + util.stackFrame.duration = Cast.toNumber(args.SECS); + util.stackFrame.startX = util.target.x; + util.stackFrame.startY = util.target.y; + util.stackFrame.endX = Cast.toNumber(args.X); + util.stackFrame.endY = Cast.toNumber(args.Y); + if (util.stackFrame.duration <= 0) { + // Duration too short to glide. + util.target.setXY(util.stackFrame.endX, util.stackFrame.endY); + return; + } util.yield(); } else { - // Finished: move to final position. - util.target.setXY(util.stackFrame.endX, util.stackFrame.endY); + const timeElapsed = util.stackFrame.timer.timeElapsed(); + if (timeElapsed < util.stackFrame.duration * 1000) { + // In progress: move to intermediate position. + const frac = timeElapsed / (util.stackFrame.duration * 1000); + const dx = frac * (util.stackFrame.endX - util.stackFrame.startX); + const dy = frac * (util.stackFrame.endY - util.stackFrame.startY); + util.target.setXY( + util.stackFrame.startX + dx, + util.stackFrame.startY + dy + ); + util.yield(); + } else { + // Finished: move to final position. + util.target.setXY(util.stackFrame.endX, util.stackFrame.endY); + } } } -}; -Scratch3MotionBlocks.prototype.ifOnEdgeBounce = function (args, util) { - const bounds = util.target.getBounds(); - if (!bounds) { - return; + ifOnEdgeBounce (args, util) { + const bounds = util.target.getBounds(); + if (!bounds) { + return; + } + // Measure distance to edges. + // Values are positive when the sprite is far away, + // and clamped to zero when the sprite is beyond. + const stageWidth = this.runtime.constructor.STAGE_WIDTH; + const stageHeight = this.runtime.constructor.STAGE_HEIGHT; + const distLeft = Math.max(0, (stageWidth / 2) + bounds.left); + const distTop = Math.max(0, (stageHeight / 2) - bounds.top); + const distRight = Math.max(0, (stageWidth / 2) - bounds.right); + const distBottom = Math.max(0, (stageHeight / 2) + bounds.bottom); + // Find the nearest edge. + let nearestEdge = ''; + let minDist = Infinity; + if (distLeft < minDist) { + minDist = distLeft; + nearestEdge = 'left'; + } + if (distTop < minDist) { + minDist = distTop; + nearestEdge = 'top'; + } + if (distRight < minDist) { + minDist = distRight; + nearestEdge = 'right'; + } + if (distBottom < minDist) { + minDist = distBottom; + nearestEdge = 'bottom'; + } + if (minDist > 0) { + return; // Not touching any edge. + } + // Point away from the nearest edge. + const radians = MathUtil.degToRad(90 - util.target.direction); + let dx = Math.cos(radians); + let dy = -Math.sin(radians); + if (nearestEdge === 'left') { + dx = Math.max(0.2, Math.abs(dx)); + } else if (nearestEdge === 'top') { + dy = Math.max(0.2, Math.abs(dy)); + } else if (nearestEdge === 'right') { + dx = 0 - Math.max(0.2, Math.abs(dx)); + } else if (nearestEdge === 'bottom') { + dy = 0 - Math.max(0.2, Math.abs(dy)); + } + const newDirection = MathUtil.radToDeg(Math.atan2(dy, dx)) + 90; + util.target.setDirection(newDirection); + // Keep within the stage. + const fencedPosition = util.target.keepInFence(util.target.x, util.target.y); + util.target.setXY(fencedPosition[0], fencedPosition[1]); } - // Measure distance to edges. - // Values are positive when the sprite is far away, - // and clamped to zero when the sprite is beyond. - const stageWidth = this.runtime.constructor.STAGE_WIDTH; - const stageHeight = this.runtime.constructor.STAGE_HEIGHT; - const distLeft = Math.max(0, (stageWidth / 2) + bounds.left); - const distTop = Math.max(0, (stageHeight / 2) - bounds.top); - const distRight = Math.max(0, (stageWidth / 2) - bounds.right); - const distBottom = Math.max(0, (stageHeight / 2) + bounds.bottom); - // Find the nearest edge. - let nearestEdge = ''; - let minDist = Infinity; - if (distLeft < minDist) { - minDist = distLeft; - nearestEdge = 'left'; + + setRotationStyle (args, util) { + util.target.setRotationStyle(args.STYLE); } - if (distTop < minDist) { - minDist = distTop; - nearestEdge = 'top'; + + changeX (args, util) { + const dx = Cast.toNumber(args.DX); + util.target.setXY(util.target.x + dx, util.target.y); } - if (distRight < minDist) { - minDist = distRight; - nearestEdge = 'right'; + + setX (args, util) { + const x = Cast.toNumber(args.X); + util.target.setXY(x, util.target.y); } - if (distBottom < minDist) { - minDist = distBottom; - nearestEdge = 'bottom'; + + changeY (args, util) { + const dy = Cast.toNumber(args.DY); + util.target.setXY(util.target.x, util.target.y + dy); } - if (minDist > 0) { - return; // Not touching any edge. + + setY (args, util) { + const y = Cast.toNumber(args.Y); + util.target.setXY(util.target.x, y); } - // Point away from the nearest edge. - const radians = MathUtil.degToRad(90 - util.target.direction); - let dx = Math.cos(radians); - let dy = -Math.sin(radians); - if (nearestEdge === 'left') { - dx = Math.max(0.2, Math.abs(dx)); - } else if (nearestEdge === 'top') { - dy = Math.max(0.2, Math.abs(dy)); - } else if (nearestEdge === 'right') { - dx = 0 - Math.max(0.2, Math.abs(dx)); - } else if (nearestEdge === 'bottom') { - dy = 0 - Math.max(0.2, Math.abs(dy)); + + getX (args, util) { + return util.target.x; } - const newDirection = MathUtil.radToDeg(Math.atan2(dy, dx)) + 90; - util.target.setDirection(newDirection); - // Keep within the stage. - const fencedPosition = util.target.keepInFence(util.target.x, util.target.y); - util.target.setXY(fencedPosition[0], fencedPosition[1]); -}; -Scratch3MotionBlocks.prototype.setRotationStyle = function (args, util) { - util.target.setRotationStyle(args.STYLE); -}; + getY (args, util) { + return util.target.y; + } -Scratch3MotionBlocks.prototype.changeX = function (args, util) { - const dx = Cast.toNumber(args.DX); - util.target.setXY(util.target.x + dx, util.target.y); -}; - -Scratch3MotionBlocks.prototype.setX = function (args, util) { - const x = Cast.toNumber(args.X); - util.target.setXY(x, util.target.y); -}; - -Scratch3MotionBlocks.prototype.changeY = function (args, util) { - const dy = Cast.toNumber(args.DY); - util.target.setXY(util.target.x, util.target.y + dy); -}; - -Scratch3MotionBlocks.prototype.setY = function (args, util) { - const y = Cast.toNumber(args.Y); - util.target.setXY(util.target.x, y); -}; - -Scratch3MotionBlocks.prototype.getX = function (args, util) { - return util.target.x; -}; - -Scratch3MotionBlocks.prototype.getY = function (args, util) { - return util.target.y; -}; - -Scratch3MotionBlocks.prototype.getDirection = function (args, util) { - return util.target.direction; -}; + getDirection (args, util) { + return util.target.direction; + } +} module.exports = Scratch3MotionBlocks; diff --git a/src/blocks/scratch3_operators.js b/src/blocks/scratch3_operators.js index 54a983c9..812fd6bb 100644 --- a/src/blocks/scratch3_operators.js +++ b/src/blocks/scratch3_operators.js @@ -1,144 +1,146 @@ const Cast = require('../util/cast.js'); const MathUtil = require('../util/math-util.js'); -const Scratch3OperatorsBlocks = function (runtime) { +class Scratch3OperatorsBlocks { + constructor (runtime) { + /** + * The runtime instantiating this block package. + * @type {Runtime} + */ + this.runtime = runtime; + } + /** - * The runtime instantiating this block package. - * @type {Runtime} + * Retrieve the block primitives implemented by this package. + * @return {object.} Mapping of opcode to Function. */ - this.runtime = runtime; -}; - -/** - * Retrieve the block primitives implemented by this package. - * @return {object.} Mapping of opcode to Function. - */ -Scratch3OperatorsBlocks.prototype.getPrimitives = function () { - return { - operator_add: this.add, - operator_subtract: this.subtract, - operator_multiply: this.multiply, - operator_divide: this.divide, - operator_lt: this.lt, - operator_equals: this.equals, - operator_gt: this.gt, - operator_and: this.and, - operator_or: this.or, - operator_not: this.not, - operator_random: this.random, - operator_join: this.join, - operator_letter_of: this.letterOf, - operator_length: this.length, - operator_mod: this.mod, - operator_round: this.round, - operator_mathop: this.mathop - }; -}; - -Scratch3OperatorsBlocks.prototype.add = function (args) { - return Cast.toNumber(args.NUM1) + Cast.toNumber(args.NUM2); -}; - -Scratch3OperatorsBlocks.prototype.subtract = function (args) { - return Cast.toNumber(args.NUM1) - Cast.toNumber(args.NUM2); -}; - -Scratch3OperatorsBlocks.prototype.multiply = function (args) { - return Cast.toNumber(args.NUM1) * Cast.toNumber(args.NUM2); -}; - -Scratch3OperatorsBlocks.prototype.divide = function (args) { - return Cast.toNumber(args.NUM1) / Cast.toNumber(args.NUM2); -}; - -Scratch3OperatorsBlocks.prototype.lt = function (args) { - return Cast.compare(args.OPERAND1, args.OPERAND2) < 0; -}; - -Scratch3OperatorsBlocks.prototype.equals = function (args) { - return Cast.compare(args.OPERAND1, args.OPERAND2) === 0; -}; - -Scratch3OperatorsBlocks.prototype.gt = function (args) { - return Cast.compare(args.OPERAND1, args.OPERAND2) > 0; -}; - -Scratch3OperatorsBlocks.prototype.and = function (args) { - return Cast.toBoolean(args.OPERAND1) && Cast.toBoolean(args.OPERAND2); -}; - -Scratch3OperatorsBlocks.prototype.or = function (args) { - return Cast.toBoolean(args.OPERAND1) || Cast.toBoolean(args.OPERAND2); -}; - -Scratch3OperatorsBlocks.prototype.not = function (args) { - return !Cast.toBoolean(args.OPERAND); -}; - -Scratch3OperatorsBlocks.prototype.random = function (args) { - const nFrom = Cast.toNumber(args.FROM); - const nTo = Cast.toNumber(args.TO); - const low = nFrom <= nTo ? nFrom : nTo; - const high = nFrom <= nTo ? nTo : nFrom; - if (low === high) return low; - // If both arguments are ints, truncate the result to an int. - if (Cast.isInt(args.FROM) && Cast.isInt(args.TO)) { - return low + parseInt(Math.random() * ((high + 1) - low), 10); + getPrimitives () { + return { + operator_add: this.add, + operator_subtract: this.subtract, + operator_multiply: this.multiply, + operator_divide: this.divide, + operator_lt: this.lt, + operator_equals: this.equals, + operator_gt: this.gt, + operator_and: this.and, + operator_or: this.or, + operator_not: this.not, + operator_random: this.random, + operator_join: this.join, + operator_letter_of: this.letterOf, + operator_length: this.length, + operator_mod: this.mod, + operator_round: this.round, + operator_mathop: this.mathop + }; } - return (Math.random() * (high - low)) + low; -}; -Scratch3OperatorsBlocks.prototype.join = function (args) { - return Cast.toString(args.STRING1) + Cast.toString(args.STRING2); -}; - -Scratch3OperatorsBlocks.prototype.letterOf = function (args) { - const index = Cast.toNumber(args.LETTER) - 1; - const str = Cast.toString(args.STRING); - // Out of bounds? - if (index < 0 || index >= str.length) { - return ''; + add (args) { + return Cast.toNumber(args.NUM1) + Cast.toNumber(args.NUM2); } - return str.charAt(index); -}; -Scratch3OperatorsBlocks.prototype.length = function (args) { - return Cast.toString(args.STRING).length; -}; - -Scratch3OperatorsBlocks.prototype.mod = function (args) { - const n = Cast.toNumber(args.NUM1); - const modulus = Cast.toNumber(args.NUM2); - let result = n % modulus; - // Scratch mod is kept positive. - if (result / modulus < 0) result += modulus; - return result; -}; - -Scratch3OperatorsBlocks.prototype.round = function (args) { - return Math.round(Cast.toNumber(args.NUM)); -}; - -Scratch3OperatorsBlocks.prototype.mathop = function (args) { - const operator = Cast.toString(args.OPERATOR).toLowerCase(); - const n = Cast.toNumber(args.NUM); - switch (operator) { - case 'abs': return Math.abs(n); - case 'floor': return Math.floor(n); - case 'ceiling': return Math.ceil(n); - case 'sqrt': return Math.sqrt(n); - case 'sin': return parseFloat(Math.sin((Math.PI * n) / 180).toFixed(10)); - case 'cos': return parseFloat(Math.cos((Math.PI * n) / 180).toFixed(10)); - case 'tan': return MathUtil.tan(n); - case 'asin': return (Math.asin(n) * 180) / Math.PI; - case 'acos': return (Math.acos(n) * 180) / Math.PI; - case 'atan': return (Math.atan(n) * 180) / Math.PI; - case 'ln': return Math.log(n); - case 'log': return Math.log(n) / Math.LN10; - case 'e ^': return Math.exp(n); - case '10 ^': return Math.pow(10, n); + subtract (args) { + return Cast.toNumber(args.NUM1) - Cast.toNumber(args.NUM2); } - return 0; -}; + + multiply (args) { + return Cast.toNumber(args.NUM1) * Cast.toNumber(args.NUM2); + } + + divide (args) { + return Cast.toNumber(args.NUM1) / Cast.toNumber(args.NUM2); + } + + lt (args) { + return Cast.compare(args.OPERAND1, args.OPERAND2) < 0; + } + + equals (args) { + return Cast.compare(args.OPERAND1, args.OPERAND2) === 0; + } + + gt (args) { + return Cast.compare(args.OPERAND1, args.OPERAND2) > 0; + } + + and (args) { + return Cast.toBoolean(args.OPERAND1) && Cast.toBoolean(args.OPERAND2); + } + + or (args) { + return Cast.toBoolean(args.OPERAND1) || Cast.toBoolean(args.OPERAND2); + } + + not (args) { + return !Cast.toBoolean(args.OPERAND); + } + + random (args) { + const nFrom = Cast.toNumber(args.FROM); + const nTo = Cast.toNumber(args.TO); + const low = nFrom <= nTo ? nFrom : nTo; + const high = nFrom <= nTo ? nTo : nFrom; + if (low === high) return low; + // If both arguments are ints, truncate the result to an int. + if (Cast.isInt(args.FROM) && Cast.isInt(args.TO)) { + return low + parseInt(Math.random() * ((high + 1) - low), 10); + } + return (Math.random() * (high - low)) + low; + } + + join (args) { + return Cast.toString(args.STRING1) + Cast.toString(args.STRING2); + } + + letterOf (args) { + const index = Cast.toNumber(args.LETTER) - 1; + const str = Cast.toString(args.STRING); + // Out of bounds? + if (index < 0 || index >= str.length) { + return ''; + } + return str.charAt(index); + } + + length (args) { + return Cast.toString(args.STRING).length; + } + + mod (args) { + const n = Cast.toNumber(args.NUM1); + const modulus = Cast.toNumber(args.NUM2); + let result = n % modulus; + // Scratch mod is kept positive. + if (result / modulus < 0) result += modulus; + return result; + } + + round (args) { + return Math.round(Cast.toNumber(args.NUM)); + } + + mathop (args) { + const operator = Cast.toString(args.OPERATOR).toLowerCase(); + const n = Cast.toNumber(args.NUM); + switch (operator) { + case 'abs': return Math.abs(n); + case 'floor': return Math.floor(n); + case 'ceiling': return Math.ceil(n); + case 'sqrt': return Math.sqrt(n); + case 'sin': return parseFloat(Math.sin((Math.PI * n) / 180).toFixed(10)); + case 'cos': return parseFloat(Math.cos((Math.PI * n) / 180).toFixed(10)); + case 'tan': return MathUtil.tan(n); + case 'asin': return (Math.asin(n) * 180) / Math.PI; + case 'acos': return (Math.acos(n) * 180) / Math.PI; + case 'atan': return (Math.atan(n) * 180) / Math.PI; + case 'ln': return Math.log(n); + case 'log': return Math.log(n) / Math.LN10; + case 'e ^': return Math.exp(n); + case '10 ^': return Math.pow(10, n); + } + return 0; + } +} module.exports = Scratch3OperatorsBlocks; diff --git a/src/blocks/scratch3_pen.js b/src/blocks/scratch3_pen.js index 4f59492d..5ba505cb 100644 --- a/src/blocks/scratch3_pen.js +++ b/src/blocks/scratch3_pen.js @@ -18,322 +18,337 @@ const RenderedTarget = require('../sprites/rendered-target'); * @param {Runtime} runtime - the runtime instantiating this block package. * @constructor */ -const Scratch3PenBlocks = function (runtime) { - /** - * The runtime instantiating this block package. - * @type {Runtime} - */ - this.runtime = runtime; +class Scratch3PenBlocks { + constructor (runtime) { + /** + * The runtime instantiating this block package. + * @type {Runtime} + */ + this.runtime = runtime; + + /** + * The ID of the renderer Drawable corresponding to the pen layer. + * @type {int} + * @private + */ + this._penDrawableId = -1; + + /** + * The ID of the renderer Skin corresponding to the pen layer. + * @type {int} + * @private + */ + this._penSkinId = -1; + + this._onTargetMoved = this._onTargetMoved.bind(this); + } /** - * The ID of the renderer Drawable corresponding to the pen layer. + * The default pen state, to be used when a target has no existing pen state. + * @type {PenState} + */ + static get DEFAULT_PEN_STATE () { + return { + penDown: false, + hue: 120, + shade: 50, + penAttributes: { + color4f: [0, 0, 1, 1], + diameter: 1 + } + }; + } + + /** + * Place the pen layer in front of the backdrop but behind everything else. + * We should probably handle this somewhere else... somewhere central that knows about pen, backdrop, video, etc. + * Maybe it should be in the GUI? * @type {int} + */ + static get PEN_ORDER () { + return 1; + } + + /** + * The minimum and maximum allowed pen size. + * @type {{min: number, max: number}} + */ + static get PEN_SIZE_RANGE () { + return {min: 1, max: 255}; + } + + /** + * The key to load & store a target's pen-related state. + * @type {string} + */ + static get STATE_KEY () { + return 'Scratch.pen'; + } + + /** + * Clamp a pen size value to the range allowed by the pen. + * @param {number} requestedSize - the requested pen size. + * @returns {number} the clamped size. * @private */ - this._penDrawableId = -1; + _clampPenSize (requestedSize) { + return MathUtil.clamp( + requestedSize, + Scratch3PenBlocks.PEN_SIZE_RANGE.min, + Scratch3PenBlocks.PEN_SIZE_RANGE.max + ); + } /** - * The ID of the renderer Skin corresponding to the pen layer. - * @type {int} + * Retrieve the ID of the renderer "Skin" corresponding to the pen layer. If + * the pen Skin doesn't yet exist, create it. + * @returns {int} the Skin ID of the pen layer, or -1 on failure. * @private */ - this._penSkinId = -1; - - this._onTargetMoved = this._onTargetMoved.bind(this); -}; - -/** - * The default pen state, to be used when a target has no existing pen state. - * @type {PenState} - */ -Scratch3PenBlocks.DEFAULT_PEN_STATE = { - penDown: false, - hue: 120, - shade: 50, - penAttributes: { - color4f: [0, 0, 1, 1], - diameter: 1 - } -}; - -/** - * Place the pen layer in front of the backdrop but behind everything else. - * We should probably handle this somewhere else... somewhere central that knows about pen, backdrop, video, etc. - * Maybe it should be in the GUI? - * @type {int} - */ -Scratch3PenBlocks.PEN_ORDER = 1; - -/** - * The minimum and maximum allowed pen size. - * @type {{min: number, max: number}} - */ -Scratch3PenBlocks.PEN_SIZE_RANGE = {min: 1, max: 255}; - -/** - * The key to load & store a target's pen-related state. - * @type {string} - */ -Scratch3PenBlocks.STATE_KEY = 'Scratch.pen'; - -/** - * Clamp a pen size value to the range allowed by the pen. - * @param {number} requestedSize - the requested pen size. - * @returns {number} the clamped size. - * @private - */ -Scratch3PenBlocks.prototype._clampPenSize = function (requestedSize) { - return MathUtil.clamp(requestedSize, Scratch3PenBlocks.PEN_SIZE_RANGE.min, Scratch3PenBlocks.PEN_SIZE_RANGE.max); -}; - -/** - * Retrieve the ID of the renderer "Skin" corresponding to the pen layer. If the pen Skin doesn't yet exist, create it. - * @returns {int} the Skin ID of the pen layer, or -1 on failure. - * @private - */ -Scratch3PenBlocks.prototype._getPenLayerID = function () { - if (this._penSkinId < 0 && this.runtime.renderer) { - this._penSkinId = this.runtime.renderer.createPenSkin(); - this._penDrawableId = this.runtime.renderer.createDrawable(); - this.runtime.renderer.setDrawableOrder(this._penDrawableId, Scratch3PenBlocks.PEN_ORDER); - this.runtime.renderer.updateDrawableProperties(this._penDrawableId, {skinId: this._penSkinId}); - } - return this._penSkinId; -}; - -/** - * @param {Target} target - collect pen state for this target. Probably, but not necessarily, a RenderedTarget. - * @returns {PenState} the mutable pen state associated with that target. This will be created if necessary. - * @private - */ -Scratch3PenBlocks.prototype._getPenState = function (target) { - let penState = target.getCustomState(Scratch3PenBlocks.STATE_KEY); - if (!penState) { - penState = Clone.simple(Scratch3PenBlocks.DEFAULT_PEN_STATE); - target.setCustomState(Scratch3PenBlocks.STATE_KEY, penState); - } - return penState; -}; - -/** - * Handle a target which has moved. This only fires when the pen is down. - * @param {RenderedTarget} target - the target which has moved. - * @param {number} oldX - the previous X position. - * @param {number} oldY - the previous Y position. - * @private - */ -Scratch3PenBlocks.prototype._onTargetMoved = function (target, oldX, oldY) { - const penSkinId = this._getPenLayerID(); - if (penSkinId >= 0) { - const penState = this._getPenState(target); - this.runtime.renderer.penLine(penSkinId, penState.penAttributes, oldX, oldY, target.x, target.y); - this.runtime.requestRedraw(); - } -}; - -/** - * Update the cached RGB color from the hue & shade values in the provided PenState object. - * @param {PenState} penState - the pen state to update. - * @private - */ -Scratch3PenBlocks.prototype._updatePenColor = function (penState) { - let rgb = Color.hsvToRgb({h: penState.hue * 180 / 100, s: 1, v: 1}); - const shade = (penState.shade > 100) ? 200 - penState.shade : penState.shade; - if (shade < 50) { - rgb = Color.mixRgb(Color.RGB_BLACK, rgb, (10 + shade) / 60); - } else { - rgb = Color.mixRgb(rgb, Color.RGB_WHITE, (shade - 50) / 60); - } - penState.penAttributes.color4f[0] = rgb.r / 255.0; - penState.penAttributes.color4f[1] = rgb.g / 255.0; - penState.penAttributes.color4f[2] = rgb.b / 255.0; - penState.penAttributes.color4f[3] = 1; -}; - -/** - * Wrap a pen hue or shade values to the range [0,200). - * @param {number} value - the pen hue or shade value to the proper range. - * @returns {number} the wrapped value. - * @private - */ -Scratch3PenBlocks.prototype._wrapHueOrShade = function (value) { - value = value % 200; - if (value < 0) value += 200; - return value; -}; - -/** - * Retrieve the block primitives implemented by this package. - * @return {object.} Mapping of opcode to Function. - */ -Scratch3PenBlocks.prototype.getPrimitives = function () { - return { - pen_clear: this.clear, - pen_stamp: this.stamp, - pen_pendown: this.penDown, - pen_penup: this.penUp, - pen_setpencolortocolor: this.setPenColorToColor, - pen_changepencolorby: this.changePenHueBy, - pen_setpencolortonum: this.setPenHueToNumber, - pen_changepenshadeby: this.changePenShadeBy, - pen_setpenshadeto: this.setPenShadeToNumber, - pen_changepensizeby: this.changePenSizeBy, - pen_setpensizeto: this.setPenSizeTo - }; -}; - -/** - * The pen "clear" block clears the pen layer's contents. - */ -Scratch3PenBlocks.prototype.clear = function () { - const penSkinId = this._getPenLayerID(); - if (penSkinId >= 0) { - this.runtime.renderer.penClear(penSkinId); - this.runtime.requestRedraw(); - } -}; - -/** - * The pen "stamp" block stamps the current drawable's image onto the pen layer. - * @param {object} args - the block arguments. - * @param {object} util - utility object provided by the runtime. - */ -Scratch3PenBlocks.prototype.stamp = function (args, util) { - const penSkinId = this._getPenLayerID(); - if (penSkinId >= 0) { - const target = util.target; - this.runtime.renderer.penStamp(penSkinId, target.drawableID); - this.runtime.requestRedraw(); - } -}; - -/** - * The pen "pen down" block causes the target to leave pen trails on future motion. - * @param {object} args - the block arguments. - * @param {object} util - utility object provided by the runtime. - */ -Scratch3PenBlocks.prototype.penDown = function (args, util) { - const target = util.target; - const penState = this._getPenState(target); - - if (!penState.penDown) { - penState.penDown = true; - target.addListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved); + _getPenLayerID () { + if (this._penSkinId < 0 && this.runtime.renderer) { + this._penSkinId = this.runtime.renderer.createPenSkin(); + this._penDrawableId = this.runtime.renderer.createDrawable(); + this.runtime.renderer.setDrawableOrder(this._penDrawableId, Scratch3PenBlocks.PEN_ORDER); + this.runtime.renderer.updateDrawableProperties(this._penDrawableId, {skinId: this._penSkinId}); + } + return this._penSkinId; } - const penSkinId = this._getPenLayerID(); - if (penSkinId >= 0) { - this.runtime.renderer.penPoint(penSkinId, penState.penAttributes, target.x, target.y); - this.runtime.requestRedraw(); + /** + * @param {Target} target - collect pen state for this target. Probably, but not necessarily, a RenderedTarget. + * @returns {PenState} the mutable pen state associated with that target. This will be created if necessary. + * @private + */ + _getPenState (target) { + let penState = target.getCustomState(Scratch3PenBlocks.STATE_KEY); + if (!penState) { + penState = Clone.simple(Scratch3PenBlocks.DEFAULT_PEN_STATE); + target.setCustomState(Scratch3PenBlocks.STATE_KEY, penState); + } + return penState; } -}; -/** - * The pen "pen up" block stops the target from leaving pen trails. - * @param {object} args - the block arguments. - * @param {object} util - utility object provided by the runtime. - */ -Scratch3PenBlocks.prototype.penUp = function (args, util) { - const target = util.target; - const penState = this._getPenState(target); - - if (penState.penDown) { - penState.penDown = false; - target.removeListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved); + /** + * Handle a target which has moved. This only fires when the pen is down. + * @param {RenderedTarget} target - the target which has moved. + * @param {number} oldX - the previous X position. + * @param {number} oldY - the previous Y position. + * @private + */ + _onTargetMoved (target, oldX, oldY) { + const penSkinId = this._getPenLayerID(); + if (penSkinId >= 0) { + const penState = this._getPenState(target); + this.runtime.renderer.penLine(penSkinId, penState.penAttributes, oldX, oldY, target.x, target.y); + this.runtime.requestRedraw(); + } } -}; -/** - * The pen "set pen color to {color}" block sets the pen to a particular RGB color. - * @param {object} args - the block arguments. - * @property {int} COLOR - the color to set, expressed as a 24-bit RGB value (0xRRGGBB). - * @param {object} util - utility object provided by the runtime. - */ -Scratch3PenBlocks.prototype.setPenColorToColor = function (args, util) { - const penState = this._getPenState(util.target); - const rgb = Cast.toRgbColorObject(args.COLOR); - const hsv = Color.rgbToHsv(rgb); - - penState.hue = 200 * hsv.h / 360; - penState.shade = 50 * hsv.v; - penState.penAttributes.color4f[0] = rgb.r / 255.0; - penState.penAttributes.color4f[1] = rgb.g / 255.0; - penState.penAttributes.color4f[2] = rgb.b / 255.0; - if (rgb.hasOwnProperty('a')) { // Will there always be an 'a'? - penState.penAttributes.color4f[3] = rgb.a / 255.0; - } else { + /** + * Update the cached RGB color from the hue & shade values in the provided PenState object. + * @param {PenState} penState - the pen state to update. + * @private + */ + _updatePenColor (penState) { + let rgb = Color.hsvToRgb({h: penState.hue * 180 / 100, s: 1, v: 1}); + const shade = (penState.shade > 100) ? 200 - penState.shade : penState.shade; + if (shade < 50) { + rgb = Color.mixRgb(Color.RGB_BLACK, rgb, (10 + shade) / 60); + } else { + rgb = Color.mixRgb(rgb, Color.RGB_WHITE, (shade - 50) / 60); + } + penState.penAttributes.color4f[0] = rgb.r / 255.0; + penState.penAttributes.color4f[1] = rgb.g / 255.0; + penState.penAttributes.color4f[2] = rgb.b / 255.0; penState.penAttributes.color4f[3] = 1; } -}; -/** - * The pen "change pen color by {number}" block rotates the hue of the pen by the given amount. - * @param {object} args - the block arguments. - * @property {number} COLOR - the amount of desired hue rotation. - * @param {object} util - utility object provided by the runtime. - */ -Scratch3PenBlocks.prototype.changePenHueBy = function (args, util) { - const penState = this._getPenState(util.target); - penState.hue = this._wrapHueOrShade(penState.hue + Cast.toNumber(args.COLOR)); - this._updatePenColor(penState); -}; + /** + * Wrap a pen hue or shade values to the range [0,200). + * @param {number} value - the pen hue or shade value to the proper range. + * @returns {number} the wrapped value. + * @private + */ + _wrapHueOrShade (value) { + value = value % 200; + if (value < 0) value += 200; + return value; + } -/** - * The pen "set pen color to {number}" block sets the hue of the pen. - * @param {object} args - the block arguments. - * @property {number} COLOR - the desired hue. - * @param {object} util - utility object provided by the runtime. - */ -Scratch3PenBlocks.prototype.setPenHueToNumber = function (args, util) { - const penState = this._getPenState(util.target); - penState.hue = this._wrapHueOrShade(Cast.toNumber(args.COLOR)); - this._updatePenColor(penState); -}; + /** + * Retrieve the block primitives implemented by this package. + * @return {object.} Mapping of opcode to Function. + */ + getPrimitives () { + return { + pen_clear: this.clear, + pen_stamp: this.stamp, + pen_pendown: this.penDown, + pen_penup: this.penUp, + pen_setpencolortocolor: this.setPenColorToColor, + pen_changepencolorby: this.changePenHueBy, + pen_setpencolortonum: this.setPenHueToNumber, + pen_changepenshadeby: this.changePenShadeBy, + pen_setpenshadeto: this.setPenShadeToNumber, + pen_changepensizeby: this.changePenSizeBy, + pen_setpensizeto: this.setPenSizeTo + }; + } -/** - * The pen "change pen shade by {number}" block changes the "shade" of the pen, related to the HSV value. - * @param {object} args - the block arguments. - * @property {number} SHADE - the amount of desired shade change. - * @param {object} util - utility object provided by the runtime. - */ -Scratch3PenBlocks.prototype.changePenShadeBy = function (args, util) { - const penState = this._getPenState(util.target); - penState.shade = this._wrapHueOrShade(penState.shade + Cast.toNumber(args.SHADE)); - this._updatePenColor(penState); -}; + /** + * The pen "clear" block clears the pen layer's contents. + */ + clear () { + const penSkinId = this._getPenLayerID(); + if (penSkinId >= 0) { + this.runtime.renderer.penClear(penSkinId); + this.runtime.requestRedraw(); + } + } -/** - * The pen "set pen shade to {number}" block sets the "shade" of the pen, related to the HSV value. - * @param {object} args - the block arguments. - * @property {number} SHADE - the amount of desired shade change. - * @param {object} util - utility object provided by the runtime. - */ -Scratch3PenBlocks.prototype.setPenShadeToNumber = function (args, util) { - const penState = this._getPenState(util.target); - penState.shade = this._wrapHueOrShade(Cast.toNumber(args.SHADE)); - this._updatePenColor(penState); -}; + /** + * The pen "stamp" block stamps the current drawable's image onto the pen layer. + * @param {object} args - the block arguments. + * @param {object} util - utility object provided by the runtime. + */ + stamp (args, util) { + const penSkinId = this._getPenLayerID(); + if (penSkinId >= 0) { + const target = util.target; + this.runtime.renderer.penStamp(penSkinId, target.drawableID); + this.runtime.requestRedraw(); + } + } -/** - * The pen "change pen size by {number}" block changes the pen size by the given amount. - * @param {object} args - the block arguments. - * @property {number} SIZE - the amount of desired size change. - * @param {object} util - utility object provided by the runtime. - */ -Scratch3PenBlocks.prototype.changePenSizeBy = function (args, util) { - const penAttributes = this._getPenState(util.target).penAttributes; - penAttributes.diameter = this._clampPenSize(penAttributes.diameter + Cast.toNumber(args.SIZE)); -}; + /** + * The pen "pen down" block causes the target to leave pen trails on future motion. + * @param {object} args - the block arguments. + * @param {object} util - utility object provided by the runtime. + */ + penDown (args, util) { + const target = util.target; + const penState = this._getPenState(target); -/** - * The pen "set pen size to {number}" block sets the pen size to the given amount. - * @param {object} args - the block arguments. - * @property {number} SIZE - the amount of desired size change. - * @param {object} util - utility object provided by the runtime. - */ -Scratch3PenBlocks.prototype.setPenSizeTo = function (args, util) { - const penAttributes = this._getPenState(util.target).penAttributes; - penAttributes.diameter = this._clampPenSize(Cast.toNumber(args.SIZE)); -}; + if (!penState.penDown) { + penState.penDown = true; + target.addListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved); + } + + const penSkinId = this._getPenLayerID(); + if (penSkinId >= 0) { + this.runtime.renderer.penPoint(penSkinId, penState.penAttributes, target.x, target.y); + this.runtime.requestRedraw(); + } + } + + /** + * The pen "pen up" block stops the target from leaving pen trails. + * @param {object} args - the block arguments. + * @param {object} util - utility object provided by the runtime. + */ + penUp (args, util) { + const target = util.target; + const penState = this._getPenState(target); + + if (penState.penDown) { + penState.penDown = false; + target.removeListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved); + } + } + + /** + * The pen "set pen color to {color}" block sets the pen to a particular RGB color. + * @param {object} args - the block arguments. + * @property {int} COLOR - the color to set, expressed as a 24-bit RGB value (0xRRGGBB). + * @param {object} util - utility object provided by the runtime. + */ + setPenColorToColor (args, util) { + const penState = this._getPenState(util.target); + const rgb = Cast.toRgbColorObject(args.COLOR); + const hsv = Color.rgbToHsv(rgb); + + penState.hue = 200 * hsv.h / 360; + penState.shade = 50 * hsv.v; + penState.penAttributes.color4f[0] = rgb.r / 255.0; + penState.penAttributes.color4f[1] = rgb.g / 255.0; + penState.penAttributes.color4f[2] = rgb.b / 255.0; + if (rgb.hasOwnProperty('a')) { // Will there always be an 'a'? + penState.penAttributes.color4f[3] = rgb.a / 255.0; + } else { + penState.penAttributes.color4f[3] = 1; + } + } + + /** + * The pen "change pen color by {number}" block rotates the hue of the pen by the given amount. + * @param {object} args - the block arguments. + * @property {number} COLOR - the amount of desired hue rotation. + * @param {object} util - utility object provided by the runtime. + */ + changePenHueBy (args, util) { + const penState = this._getPenState(util.target); + penState.hue = this._wrapHueOrShade(penState.hue + Cast.toNumber(args.COLOR)); + this._updatePenColor(penState); + } + + /** + * The pen "set pen color to {number}" block sets the hue of the pen. + * @param {object} args - the block arguments. + * @property {number} COLOR - the desired hue. + * @param {object} util - utility object provided by the runtime. + */ + setPenHueToNumber (args, util) { + const penState = this._getPenState(util.target); + penState.hue = this._wrapHueOrShade(Cast.toNumber(args.COLOR)); + this._updatePenColor(penState); + } + + /** + * The pen "change pen shade by {number}" block changes the "shade" of the pen, related to the HSV value. + * @param {object} args - the block arguments. + * @property {number} SHADE - the amount of desired shade change. + * @param {object} util - utility object provided by the runtime. + */ + changePenShadeBy (args, util) { + const penState = this._getPenState(util.target); + penState.shade = this._wrapHueOrShade(penState.shade + Cast.toNumber(args.SHADE)); + this._updatePenColor(penState); + } + + /** + * The pen "set pen shade to {number}" block sets the "shade" of the pen, related to the HSV value. + * @param {object} args - the block arguments. + * @property {number} SHADE - the amount of desired shade change. + * @param {object} util - utility object provided by the runtime. + */ + setPenShadeToNumber (args, util) { + const penState = this._getPenState(util.target); + penState.shade = this._wrapHueOrShade(Cast.toNumber(args.SHADE)); + this._updatePenColor(penState); + } + + /** + * The pen "change pen size by {number}" block changes the pen size by the given amount. + * @param {object} args - the block arguments. + * @property {number} SIZE - the amount of desired size change. + * @param {object} util - utility object provided by the runtime. + */ + changePenSizeBy (args, util) { + const penAttributes = this._getPenState(util.target).penAttributes; + penAttributes.diameter = this._clampPenSize(penAttributes.diameter + Cast.toNumber(args.SIZE)); + } + + /** + * The pen "set pen size to {number}" block sets the pen size to the given amount. + * @param {object} args - the block arguments. + * @property {number} SIZE - the amount of desired size change. + * @param {object} util - utility object provided by the runtime. + */ + setPenSizeTo (args, util) { + const penAttributes = this._getPenState(util.target).penAttributes; + penAttributes.diameter = this._clampPenSize(Cast.toNumber(args.SIZE)); + } +} module.exports = Scratch3PenBlocks; diff --git a/src/blocks/scratch3_procedures.js b/src/blocks/scratch3_procedures.js index 549ecb3c..c648d960 100644 --- a/src/blocks/scratch3_procedures.js +++ b/src/blocks/scratch3_procedures.js @@ -1,44 +1,46 @@ -const Scratch3ProcedureBlocks = function (runtime) { - /** - * The runtime instantiating this block package. - * @type {Runtime} - */ - this.runtime = runtime; -}; - -/** - * Retrieve the block primitives implemented by this package. - * @return {object.} Mapping of opcode to Function. - */ -Scratch3ProcedureBlocks.prototype.getPrimitives = function () { - return { - procedures_defnoreturn: this.defNoReturn, - procedures_callnoreturn: this.callNoReturn, - procedures_param: this.param - }; -}; - -Scratch3ProcedureBlocks.prototype.defNoReturn = function () { - // No-op: execute the blocks. -}; - -Scratch3ProcedureBlocks.prototype.callNoReturn = function (args, util) { - if (!util.stackFrame.executed) { - const procedureCode = args.mutation.proccode; - const paramNames = util.getProcedureParamNames(procedureCode); - for (let i = 0; i < paramNames.length; i++) { - if (args.hasOwnProperty(`input${i}`)) { - util.pushParam(paramNames[i], args[`input${i}`]); - } - } - util.stackFrame.executed = true; - util.startProcedure(procedureCode); +class Scratch3ProcedureBlocks { + constructor (runtime) { + /** + * The runtime instantiating this block package. + * @type {Runtime} + */ + this.runtime = runtime; } -}; -Scratch3ProcedureBlocks.prototype.param = function (args, util) { - const value = util.getParam(args.mutation.paramname); - return value; -}; + /** + * Retrieve the block primitives implemented by this package. + * @return {object.} Mapping of opcode to Function. + */ + getPrimitives () { + return { + procedures_defnoreturn: this.defNoReturn, + procedures_callnoreturn: this.callNoReturn, + procedures_param: this.param + }; + } + + defNoReturn () { + // No-op: execute the blocks. + } + + callNoReturn (args, util) { + if (!util.stackFrame.executed) { + const procedureCode = args.mutation.proccode; + const paramNames = util.getProcedureParamNames(procedureCode); + for (let i = 0; i < paramNames.length; i++) { + if (args.hasOwnProperty(`input${i}`)) { + util.pushParam(paramNames[i], args[`input${i}`]); + } + } + util.stackFrame.executed = true; + util.startProcedure(procedureCode); + } + } + + param (args, util) { + const value = util.getParam(args.mutation.paramname); + return value; + } +} module.exports = Scratch3ProcedureBlocks; diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index 27234284..e01f0cfd 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -1,177 +1,179 @@ const Cast = require('../util/cast'); -const Scratch3SensingBlocks = function (runtime) { +class Scratch3SensingBlocks { + constructor (runtime) { + /** + * The runtime instantiating this block package. + * @type {Runtime} + */ + this.runtime = runtime; + } + /** - * The runtime instantiating this block package. - * @type {Runtime} + * Retrieve the block primitives implemented by this package. + * @return {object.} Mapping of opcode to Function. */ - this.runtime = runtime; -}; - -/** - * Retrieve the block primitives implemented by this package. - * @return {object.} Mapping of opcode to Function. - */ -Scratch3SensingBlocks.prototype.getPrimitives = function () { - return { - sensing_touchingobject: this.touchingObject, - sensing_touchingcolor: this.touchingColor, - sensing_coloristouchingcolor: this.colorTouchingColor, - sensing_distanceto: this.distanceTo, - sensing_timer: this.getTimer, - sensing_resettimer: this.resetTimer, - sensing_of: this.getAttributeOf, - sensing_mousex: this.getMouseX, - sensing_mousey: this.getMouseY, - sensing_mousedown: this.getMouseDown, - sensing_keypressed: this.getKeyPressed, - sensing_current: this.current, - sensing_dayssince2000: this.daysSince2000, - sensing_loudness: this.getLoudness - }; -}; - -Scratch3SensingBlocks.prototype.touchingObject = function (args, util) { - const requestedObject = args.TOUCHINGOBJECTMENU; - if (requestedObject === '_mouse_') { - const mouseX = util.ioQuery('mouse', 'getX'); - const mouseY = util.ioQuery('mouse', 'getY'); - return util.target.isTouchingPoint(mouseX, mouseY); - } else if (requestedObject === '_edge_') { - return util.target.isTouchingEdge(); - } - return util.target.isTouchingSprite(requestedObject); - -}; - -Scratch3SensingBlocks.prototype.touchingColor = function (args, util) { - const color = Cast.toRgbColorList(args.COLOR); - return util.target.isTouchingColor(color); -}; - -Scratch3SensingBlocks.prototype.colorTouchingColor = function (args, util) { - const maskColor = Cast.toRgbColorList(args.COLOR); - const targetColor = Cast.toRgbColorList(args.COLOR2); - return util.target.colorIsTouchingColor(targetColor, maskColor); -}; - -Scratch3SensingBlocks.prototype.distanceTo = function (args, util) { - if (util.target.isStage) return 10000; - - let targetX = 0; - let targetY = 0; - if (args.DISTANCETOMENU === '_mouse_') { - targetX = util.ioQuery('mouse', 'getX'); - targetY = util.ioQuery('mouse', 'getY'); - } else { - const distTarget = this.runtime.getSpriteTargetByName( - args.DISTANCETOMENU - ); - if (!distTarget) return 10000; - targetX = distTarget.x; - targetY = distTarget.y; + getPrimitives () { + return { + sensing_touchingobject: this.touchingObject, + sensing_touchingcolor: this.touchingColor, + sensing_coloristouchingcolor: this.colorTouchingColor, + sensing_distanceto: this.distanceTo, + sensing_timer: this.getTimer, + sensing_resettimer: this.resetTimer, + sensing_of: this.getAttributeOf, + sensing_mousex: this.getMouseX, + sensing_mousey: this.getMouseY, + sensing_mousedown: this.getMouseDown, + sensing_keypressed: this.getKeyPressed, + sensing_current: this.current, + sensing_dayssince2000: this.daysSince2000, + sensing_loudness: this.getLoudness + }; } - const dx = util.target.x - targetX; - const dy = util.target.y - targetY; - return Math.sqrt((dx * dx) + (dy * dy)); -}; - -Scratch3SensingBlocks.prototype.getTimer = function (args, util) { - return util.ioQuery('clock', 'projectTimer'); -}; - -Scratch3SensingBlocks.prototype.resetTimer = function (args, util) { - util.ioQuery('clock', 'resetProjectTimer'); -}; - -Scratch3SensingBlocks.prototype.getMouseX = function (args, util) { - return util.ioQuery('mouse', 'getX'); -}; - -Scratch3SensingBlocks.prototype.getMouseY = function (args, util) { - return util.ioQuery('mouse', 'getY'); -}; - -Scratch3SensingBlocks.prototype.getMouseDown = function (args, util) { - return util.ioQuery('mouse', 'getIsDown'); -}; - -Scratch3SensingBlocks.prototype.current = function (args) { - const menuOption = Cast.toString(args.CURRENTMENU).toLowerCase(); - const date = new Date(); - switch (menuOption) { - case 'year': return date.getFullYear(); - case 'month': return date.getMonth() + 1; // getMonth is zero-based - case 'date': return date.getDate(); - case 'dayofweek': return date.getDay() + 1; // getDay is zero-based, Sun=0 - case 'hour': return date.getHours(); - case 'minute': return date.getMinutes(); - case 'second': return date.getSeconds(); - } - return 0; -}; - -Scratch3SensingBlocks.prototype.getKeyPressed = function (args, util) { - return util.ioQuery('keyboard', 'getKeyIsDown', args.KEY_OPTION); -}; - -Scratch3SensingBlocks.prototype.daysSince2000 = function () { - const msPerDay = 24 * 60 * 60 * 1000; - const start = new Date(2000, 0, 1); // Months are 0-indexed. - const today = new Date(); - const dstAdjust = today.getTimezoneOffset() - start.getTimezoneOffset(); - let mSecsSinceStart = today.valueOf() - start.valueOf(); - mSecsSinceStart += ((today.getTimezoneOffset() - dstAdjust) * 60 * 1000); - return mSecsSinceStart / msPerDay; -}; - -Scratch3SensingBlocks.prototype.getLoudness = function () { - if (typeof this.runtime.audioEngine === 'undefined') return -1; - return this.runtime.audioEngine.getLoudness(); -}; - -Scratch3SensingBlocks.prototype.getAttributeOf = function (args) { - let attrTarget; - - if (args.OBJECT === '_stage_') { - attrTarget = this.runtime.getTargetForStage(); - } else { - attrTarget = this.runtime.getSpriteTargetByName(args.OBJECT); - } - - // Generic attributes - if (attrTarget.isStage) { - switch (args.PROPERTY) { - // Scratch 1.4 support - case 'background #': return attrTarget.currentCostume + 1; - - case 'backdrop #': return attrTarget.currentCostume + 1; - case 'backdrop name': - return attrTarget.sprite.costumes[attrTarget.currentCostume].name; - case 'volume': return; // @todo: Keep this in mind for sound blocks! + touchingObject (args, util) { + const requestedObject = args.TOUCHINGOBJECTMENU; + if (requestedObject === '_mouse_') { + const mouseX = util.ioQuery('mouse', 'getX'); + const mouseY = util.ioQuery('mouse', 'getY'); + return util.target.isTouchingPoint(mouseX, mouseY); + } else if (requestedObject === '_edge_') { + return util.target.isTouchingEdge(); } - } else { - switch (args.PROPERTY) { - case 'x position': return attrTarget.x; - case 'y position': return attrTarget.y; - case 'direction': return attrTarget.direction; - case 'costume #': return attrTarget.currentCostume + 1; - case 'costume name': - return attrTarget.sprite.costumes[attrTarget.currentCostume].name; - case 'size': return attrTarget.size; - case 'volume': return; // @todo: above, keep in mind for sound blocks.. + return util.target.isTouchingSprite(requestedObject); + + } + + touchingColor (args, util) { + const color = Cast.toRgbColorList(args.COLOR); + return util.target.isTouchingColor(color); + } + + colorTouchingColor (args, util) { + const maskColor = Cast.toRgbColorList(args.COLOR); + const targetColor = Cast.toRgbColorList(args.COLOR2); + return util.target.colorIsTouchingColor(targetColor, maskColor); + } + + distanceTo (args, util) { + if (util.target.isStage) return 10000; + + let targetX = 0; + let targetY = 0; + if (args.DISTANCETOMENU === '_mouse_') { + targetX = util.ioQuery('mouse', 'getX'); + targetY = util.ioQuery('mouse', 'getY'); + } else { + const distTarget = this.runtime.getSpriteTargetByName( + args.DISTANCETOMENU + ); + if (!distTarget) return 10000; + targetX = distTarget.x; + targetY = distTarget.y; } + + const dx = util.target.x - targetX; + const dy = util.target.y - targetY; + return Math.sqrt((dx * dx) + (dy * dy)); } - // Variables - const varName = args.PROPERTY; - if (attrTarget.variables.hasOwnProperty(varName)) { - return attrTarget.variables[varName].value; + getTimer (args, util) { + return util.ioQuery('clock', 'projectTimer'); } - // Otherwise, 0 - return 0; -}; + resetTimer (args, util) { + util.ioQuery('clock', 'resetProjectTimer'); + } + + getMouseX (args, util) { + return util.ioQuery('mouse', 'getX'); + } + + getMouseY (args, util) { + return util.ioQuery('mouse', 'getY'); + } + + getMouseDown (args, util) { + return util.ioQuery('mouse', 'getIsDown'); + } + + current (args) { + const menuOption = Cast.toString(args.CURRENTMENU).toLowerCase(); + const date = new Date(); + switch (menuOption) { + case 'year': return date.getFullYear(); + case 'month': return date.getMonth() + 1; // getMonth is zero-based + case 'date': return date.getDate(); + case 'dayofweek': return date.getDay() + 1; // getDay is zero-based, Sun=0 + case 'hour': return date.getHours(); + case 'minute': return date.getMinutes(); + case 'second': return date.getSeconds(); + } + return 0; + } + + getKeyPressed (args, util) { + return util.ioQuery('keyboard', 'getKeyIsDown', args.KEY_OPTION); + } + + daysSince2000 () { + const msPerDay = 24 * 60 * 60 * 1000; + const start = new Date(2000, 0, 1); // Months are 0-indexed. + const today = new Date(); + const dstAdjust = today.getTimezoneOffset() - start.getTimezoneOffset(); + let mSecsSinceStart = today.valueOf() - start.valueOf(); + mSecsSinceStart += ((today.getTimezoneOffset() - dstAdjust) * 60 * 1000); + return mSecsSinceStart / msPerDay; + } + + getLoudness () { + if (typeof this.runtime.audioEngine === 'undefined') return -1; + return this.runtime.audioEngine.getLoudness(); + } + + getAttributeOf (args) { + let attrTarget; + + if (args.OBJECT === '_stage_') { + attrTarget = this.runtime.getTargetForStage(); + } else { + attrTarget = this.runtime.getSpriteTargetByName(args.OBJECT); + } + + // Generic attributes + if (attrTarget.isStage) { + switch (args.PROPERTY) { + // Scratch 1.4 support + case 'background #': return attrTarget.currentCostume + 1; + + case 'backdrop #': return attrTarget.currentCostume + 1; + case 'backdrop name': + return attrTarget.sprite.costumes[attrTarget.currentCostume].name; + case 'volume': return; // @todo: Keep this in mind for sound blocks! + } + } else { + switch (args.PROPERTY) { + case 'x position': return attrTarget.x; + case 'y position': return attrTarget.y; + case 'direction': return attrTarget.direction; + case 'costume #': return attrTarget.currentCostume + 1; + case 'costume name': + return attrTarget.sprite.costumes[attrTarget.currentCostume].name; + case 'size': return attrTarget.size; + case 'volume': return; // @todo: above, keep in mind for sound blocks.. + } + } + + // Variables + const varName = args.PROPERTY; + if (attrTarget.variables.hasOwnProperty(varName)) { + return attrTarget.variables[varName].value; + } + + // Otherwise, 0 + return 0; + } +} module.exports = Scratch3SensingBlocks; diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 098c1c0f..8f9852ff 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -2,303 +2,317 @@ const MathUtil = require('../util/math-util'); const Cast = require('../util/cast'); const Clone = require('../util/clone'); -const Scratch3SoundBlocks = function (runtime) { +class Scratch3SoundBlocks { + constructor (runtime) { + /** + * The runtime instantiating this block package. + * @type {Runtime} + */ + this.runtime = runtime; + } + /** - * The runtime instantiating this block package. - * @type {Runtime} + * The key to load & store a target's sound-related state. + * @type {string} */ - this.runtime = runtime; -}; - -/** - * The key to load & store a target's sound-related state. - * @type {string} - */ -Scratch3SoundBlocks.STATE_KEY = 'Scratch.sound'; - -/** - * The default sound-related state, to be used when a target has no existing sound state. - * @type {SoundState} - */ -Scratch3SoundBlocks.DEFAULT_SOUND_STATE = { - volume: 100, - currentInstrument: 0, - effects: { - pitch: 0, - pan: 0, - echo: 0, - reverb: 0, - fuzz: 0, - robot: 0 - } -}; - -/** - * The minimum and maximum MIDI note numbers, for clamping the input to play note. - * @type {{min: number, max: number}} - */ -Scratch3SoundBlocks.MIDI_NOTE_RANGE = {min: 36, max: 96}; // C2 to C7 - -/** - * The minimum and maximum beat values, for clamping the duration of play note, play drum and rest. - * 100 beats at the default tempo of 60bpm is 100 seconds. - * @type {{min: number, max: number}} - */ -Scratch3SoundBlocks.BEAT_RANGE = {min: 0, max: 100}; - - /** The minimum and maximum tempo values, in bpm. - * @type {{min: number, max: number}} - */ -Scratch3SoundBlocks.TEMPO_RANGE = {min: 20, max: 500}; - - /** The minimum and maximum values for each sound effect. - * @type {{effect:{min: number, max: number}}} - */ -Scratch3SoundBlocks.EFFECT_RANGE = { - pitch: {min: -600, max: 600}, // -5 to 5 octaves - pan: {min: -100, max: 100}, // 100% left to 100% right - echo: {min: 0, max: 100}, // 0 to max (75%) feedback - reverb: {min: 0, max: 100}, // wet/dry: 0 to 100% wet - fuzz: {min: 0, max: 100}, // wed/dry: 0 to 100% wet - robot: {min: 0, max: 600} // 0 to 5 octaves -}; - -/** - * @param {Target} target - collect sound state for this target. - * @returns {SoundState} the mutable sound state associated with that target. This will be created if necessary. - * @private - */ -Scratch3SoundBlocks.prototype._getSoundState = function (target) { - let soundState = target.getCustomState(Scratch3SoundBlocks.STATE_KEY); - if (!soundState) { - soundState = Clone.simple(Scratch3SoundBlocks.DEFAULT_SOUND_STATE); - target.setCustomState(Scratch3SoundBlocks.STATE_KEY, soundState); - } - return soundState; -}; - -/** - * Retrieve the block primitives implemented by this package. - * @return {object.} Mapping of opcode to Function. - */ -Scratch3SoundBlocks.prototype.getPrimitives = function () { - return { - sound_play: this.playSound, - sound_playuntildone: this.playSoundAndWait, - sound_stopallsounds: this.stopAllSounds, - sound_playnoteforbeats: this.playNoteForBeats, - sound_playdrumforbeats: this.playDrumForBeats, - sound_restforbeats: this.restForBeats, - sound_setinstrumentto: this.setInstrument, - sound_seteffectto: this.setEffect, - sound_changeeffectby: this.changeEffect, - sound_cleareffects: this.clearEffects, - sound_sounds_menu: this.soundsMenu, - sound_beats_menu: this.beatsMenu, - sound_effects_menu: this.effectsMenu, - sound_setvolumeto: this.setVolume, - sound_changevolumeby: this.changeVolume, - sound_volume: this.getVolume, - sound_settempotobpm: this.setTempo, - sound_changetempoby: this.changeTempo, - sound_tempo: this.getTempo - }; -}; - -Scratch3SoundBlocks.prototype.playSound = function (args, util) { - const index = this._getSoundIndex(args.SOUND_MENU, util); - if (index >= 0) { - const md5 = util.target.sprite.sounds[index].md5; - if (util.target.audioPlayer === null) return; - util.target.audioPlayer.playSound(md5); - } -}; - -Scratch3SoundBlocks.prototype.playSoundAndWait = function (args, util) { - const index = this._getSoundIndex(args.SOUND_MENU, util); - if (index >= 0) { - const md5 = util.target.sprite.sounds[index].md5; - if (util.target.audioPlayer === null) return; - return util.target.audioPlayer.playSound(md5); - } -}; - -Scratch3SoundBlocks.prototype._getSoundIndex = function (soundName, util) { - // if the sprite has no sounds, return -1 - const len = util.target.sprite.sounds.length; - if (len === 0) { - return -1; + static get STATE_KEY () { + return 'Scratch.sound'; } - let index; + /** + * The default sound-related state, to be used when a target has no existing sound state. + * @type {SoundState} + */ + static get DEFAULT_SOUND_STATE () { + return { + volume: 100, + currentInstrument: 0, + effects: { + pitch: 0, + pan: 0, + echo: 0, + reverb: 0, + fuzz: 0, + robot: 0 + } + }; + } - // try to convert to a number and use that as an index - const num = parseInt(soundName, 10); - if (!isNaN(num)) { - index = MathUtil.wrapClamp(num, 0, len - 1); + /** + * The minimum and maximum MIDI note numbers, for clamping the input to play note. + * @type {{min: number, max: number}} + */ + static get MIDI_NOTE_RANGE () { + return {min: 36, max: 96}; // C2 to C7 + } + + /** + * The minimum and maximum beat values, for clamping the duration of play note, play drum and rest. + * 100 beats at the default tempo of 60bpm is 100 seconds. + * @type {{min: number, max: number}} + */ + static get BEAT_RANGE () { + return {min: 0, max: 100}; + } + + /** The minimum and maximum tempo values, in bpm. + * @type {{min: number, max: number}} + */ + static get TEMPO_RANGE () { + return {min: 20, max: 500}; + } + + /** The minimum and maximum values for each sound effect. + * @type {{effect:{min: number, max: number}}} + */ + static get EFFECT_RANGE () { + return { + pitch: {min: -600, max: 600}, // -5 to 5 octaves + pan: {min: -100, max: 100}, // 100% left to 100% right + echo: {min: 0, max: 100}, // 0 to max (75%) feedback + reverb: {min: 0, max: 100}, // wet/dry: 0 to 100% wet + fuzz: {min: 0, max: 100}, // wed/dry: 0 to 100% wet + robot: {min: 0, max: 600} // 0 to 5 octaves + }; + } + + /** + * @param {Target} target - collect sound state for this target. + * @returns {SoundState} the mutable sound state associated with that target. This will be created if necessary. + * @private + */ + _getSoundState (target) { + let soundState = target.getCustomState(Scratch3SoundBlocks.STATE_KEY); + if (!soundState) { + soundState = Clone.simple(Scratch3SoundBlocks.DEFAULT_SOUND_STATE); + target.setCustomState(Scratch3SoundBlocks.STATE_KEY, soundState); + } + return soundState; + } + + /** + * Retrieve the block primitives implemented by this package. + * @return {object.} Mapping of opcode to Function. + */ + getPrimitives () { + return { + sound_play: this.playSound, + sound_playuntildone: this.playSoundAndWait, + sound_stopallsounds: this.stopAllSounds, + sound_playnoteforbeats: this.playNoteForBeats, + sound_playdrumforbeats: this.playDrumForBeats, + sound_restforbeats: this.restForBeats, + sound_setinstrumentto: this.setInstrument, + sound_seteffectto: this.setEffect, + sound_changeeffectby: this.changeEffect, + sound_cleareffects: this.clearEffects, + sound_sounds_menu: this.soundsMenu, + sound_beats_menu: this.beatsMenu, + sound_effects_menu: this.effectsMenu, + sound_setvolumeto: this.setVolume, + sound_changevolumeby: this.changeVolume, + sound_volume: this.getVolume, + sound_settempotobpm: this.setTempo, + sound_changetempoby: this.changeTempo, + sound_tempo: this.getTempo + }; + } + + playSound (args, util) { + const index = this._getSoundIndex(args.SOUND_MENU, util); + if (index >= 0) { + const md5 = util.target.sprite.sounds[index].md5; + if (util.target.audioPlayer === null) return; + util.target.audioPlayer.playSound(md5); + } + } + + playSoundAndWait (args, util) { + const index = this._getSoundIndex(args.SOUND_MENU, util); + if (index >= 0) { + const md5 = util.target.sprite.sounds[index].md5; + if (util.target.audioPlayer === null) return; + return util.target.audioPlayer.playSound(md5); + } + } + + _getSoundIndex (soundName, util) { + // if the sprite has no sounds, return -1 + const len = util.target.sprite.sounds.length; + if (len === 0) { + return -1; + } + + let index; + + // try to convert to a number and use that as an index + const num = parseInt(soundName, 10); + if (!isNaN(num)) { + index = MathUtil.wrapClamp(num, 0, len - 1); + return index; + } + + // return the index for the sound of that name + index = this.getSoundIndexByName(soundName, util); return index; } - // return the index for the sound of that name - index = this.getSoundIndexByName(soundName, util); - return index; -}; - -Scratch3SoundBlocks.prototype.getSoundIndexByName = function (soundName, util) { - const sounds = util.target.sprite.sounds; - for (let i = 0; i < sounds.length; i++) { - if (sounds[i].name === soundName) { - return i; + getSoundIndexByName (soundName, util) { + const sounds = util.target.sprite.sounds; + for (let i = 0; i < sounds.length; i++) { + if (sounds[i].name === soundName) { + return i; + } } - } - // if there is no sound by that name, return -1 - return -1; -}; - -Scratch3SoundBlocks.prototype.stopAllSounds = function (args, util) { - if (util.target.audioPlayer === null) return; - util.target.audioPlayer.stopAllSounds(); -}; - -Scratch3SoundBlocks.prototype.playNoteForBeats = function (args, util) { - let note = Cast.toNumber(args.NOTE); - note = MathUtil.clamp(note, Scratch3SoundBlocks.MIDI_NOTE_RANGE.min, Scratch3SoundBlocks.MIDI_NOTE_RANGE.max); - let beats = Cast.toNumber(args.BEATS); - beats = this._clampBeats(beats); - const soundState = this._getSoundState(util.target); - const inst = soundState.currentInstrument; - const vol = soundState.volume; - if (typeof this.runtime.audioEngine === 'undefined') return; - return this.runtime.audioEngine.playNoteForBeatsWithInstAndVol(note, beats, inst, vol); -}; - -Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) { - let drum = Cast.toNumber(args.DRUM); - drum -= 1; // drums are one-indexed - if (typeof this.runtime.audioEngine === 'undefined') return; - drum = MathUtil.wrapClamp(drum, 0, this.runtime.audioEngine.numDrums); - let beats = Cast.toNumber(args.BEATS); - beats = this._clampBeats(beats); - if (util.target.audioPlayer === null) return; - return util.target.audioPlayer.playDrumForBeats(drum, beats); -}; - -Scratch3SoundBlocks.prototype.restForBeats = function (args) { - let beats = Cast.toNumber(args.BEATS); - beats = this._clampBeats(beats); - if (typeof this.runtime.audioEngine === 'undefined') return; - return this.runtime.audioEngine.waitForBeats(beats); -}; - -Scratch3SoundBlocks.prototype._clampBeats = function (beats) { - return MathUtil.clamp(beats, Scratch3SoundBlocks.BEAT_RANGE.min, Scratch3SoundBlocks.BEAT_RANGE.max); -}; - -Scratch3SoundBlocks.prototype.setInstrument = function (args, util) { - const soundState = this._getSoundState(util.target); - let instNum = Cast.toNumber(args.INSTRUMENT); - instNum -= 1; // instruments are one-indexed - if (typeof this.runtime.audioEngine === 'undefined') return; - instNum = MathUtil.wrapClamp(instNum, 0, this.runtime.audioEngine.numInstruments); - soundState.currentInstrument = instNum; - return this.runtime.audioEngine.instrumentPlayer.loadInstrument(soundState.currentInstrument); -}; - -Scratch3SoundBlocks.prototype.setEffect = function (args, util) { - this._updateEffect(args, util, false); -}; - -Scratch3SoundBlocks.prototype.changeEffect = function (args, util) { - this._updateEffect(args, util, true); -}; - -Scratch3SoundBlocks.prototype._updateEffect = function (args, util, change) { - const effect = Cast.toString(args.EFFECT).toLowerCase(); - const value = Cast.toNumber(args.VALUE); - - const soundState = this._getSoundState(util.target); - if (!soundState.effects.hasOwnProperty(effect)) return; - - if (change) { - soundState.effects[effect] += value; - } else { - soundState.effects[effect] = value; + // if there is no sound by that name, return -1 + return -1; } - const effectRange = Scratch3SoundBlocks.EFFECT_RANGE[effect]; - soundState.effects[effect] = MathUtil.clamp(soundState.effects[effect], effectRange.min, effectRange.max); - - if (util.target.audioPlayer === null) return; - util.target.audioPlayer.setEffect(effect, soundState.effects[effect]); -}; - -Scratch3SoundBlocks.prototype.clearEffects = function (args, util) { - const soundState = this._getSoundState(util.target); - for (const effect in soundState.effects) { - if (!soundState.effects.hasOwnProperty(effect)) continue; - soundState.effects[effect] = 0; + stopAllSounds (args, util) { + if (util.target.audioPlayer === null) return; + util.target.audioPlayer.stopAllSounds(); } - if (util.target.audioPlayer === null) return; - util.target.audioPlayer.clearEffects(); -}; -Scratch3SoundBlocks.prototype.setVolume = function (args, util) { - const volume = Cast.toNumber(args.VOLUME); - this._updateVolume(volume, util); -}; + playNoteForBeats (args, util) { + let note = Cast.toNumber(args.NOTE); + note = MathUtil.clamp(note, Scratch3SoundBlocks.MIDI_NOTE_RANGE.min, Scratch3SoundBlocks.MIDI_NOTE_RANGE.max); + let beats = Cast.toNumber(args.BEATS); + beats = this._clampBeats(beats); + const soundState = this._getSoundState(util.target); + const inst = soundState.currentInstrument; + const vol = soundState.volume; + if (typeof this.runtime.audioEngine === 'undefined') return; + return this.runtime.audioEngine.playNoteForBeatsWithInstAndVol(note, beats, inst, vol); + } -Scratch3SoundBlocks.prototype.changeVolume = function (args, util) { - const soundState = this._getSoundState(util.target); - const volume = Cast.toNumber(args.VOLUME) + soundState.volume; - this._updateVolume(volume, util); -}; + playDrumForBeats (args, util) { + let drum = Cast.toNumber(args.DRUM); + drum -= 1; // drums are one-indexed + if (typeof this.runtime.audioEngine === 'undefined') return; + drum = MathUtil.wrapClamp(drum, 0, this.runtime.audioEngine.numDrums); + let beats = Cast.toNumber(args.BEATS); + beats = this._clampBeats(beats); + if (util.target.audioPlayer === null) return; + return util.target.audioPlayer.playDrumForBeats(drum, beats); + } -Scratch3SoundBlocks.prototype._updateVolume = function (volume, util) { - const soundState = this._getSoundState(util.target); - volume = MathUtil.clamp(volume, 0, 100); - soundState.volume = volume; - if (util.target.audioPlayer === null) return; - util.target.audioPlayer.setVolume(soundState.volume); -}; + restForBeats (args) { + let beats = Cast.toNumber(args.BEATS); + beats = this._clampBeats(beats); + if (typeof this.runtime.audioEngine === 'undefined') return; + return this.runtime.audioEngine.waitForBeats(beats); + } -Scratch3SoundBlocks.prototype.getVolume = function (args, util) { - const soundState = this._getSoundState(util.target); - return soundState.volume; -}; + _clampBeats (beats) { + return MathUtil.clamp(beats, Scratch3SoundBlocks.BEAT_RANGE.min, Scratch3SoundBlocks.BEAT_RANGE.max); + } -Scratch3SoundBlocks.prototype.setTempo = function (args) { - const tempo = Cast.toNumber(args.TEMPO); - this._updateTempo(tempo); -}; + setInstrument (args, util) { + const soundState = this._getSoundState(util.target); + let instNum = Cast.toNumber(args.INSTRUMENT); + instNum -= 1; // instruments are one-indexed + if (typeof this.runtime.audioEngine === 'undefined') return; + instNum = MathUtil.wrapClamp(instNum, 0, this.runtime.audioEngine.numInstruments); + soundState.currentInstrument = instNum; + return this.runtime.audioEngine.instrumentPlayer.loadInstrument(soundState.currentInstrument); + } -Scratch3SoundBlocks.prototype.changeTempo = function (args) { - const change = Cast.toNumber(args.TEMPO); - if (typeof this.runtime.audioEngine === 'undefined') return; - const tempo = change + this.runtime.audioEngine.currentTempo; - this._updateTempo(tempo); -}; + setEffect (args, util) { + this._updateEffect(args, util, false); + } -Scratch3SoundBlocks.prototype._updateTempo = function (tempo) { - tempo = MathUtil.clamp(tempo, Scratch3SoundBlocks.TEMPO_RANGE.min, Scratch3SoundBlocks.TEMPO_RANGE.max); - if (typeof this.runtime.audioEngine === 'undefined') return; - this.runtime.audioEngine.setTempo(tempo); -}; + changeEffect (args, util) { + this._updateEffect(args, util, true); + } -Scratch3SoundBlocks.prototype.getTempo = function () { - if (typeof this.runtime.audioEngine === 'undefined') return; - return this.runtime.audioEngine.currentTempo; -}; + _updateEffect (args, util, change) { + const effect = Cast.toString(args.EFFECT).toLowerCase(); + const value = Cast.toNumber(args.VALUE); -Scratch3SoundBlocks.prototype.soundsMenu = function (args) { - return args.SOUND_MENU; -}; + const soundState = this._getSoundState(util.target); + if (!soundState.effects.hasOwnProperty(effect)) return; -Scratch3SoundBlocks.prototype.beatsMenu = function (args) { - return args.BEATS; -}; + if (change) { + soundState.effects[effect] += value; + } else { + soundState.effects[effect] = value; + } -Scratch3SoundBlocks.prototype.effectsMenu = function (args) { - return args.EFFECT; -}; + const effectRange = Scratch3SoundBlocks.EFFECT_RANGE[effect]; + soundState.effects[effect] = MathUtil.clamp(soundState.effects[effect], effectRange.min, effectRange.max); + + if (util.target.audioPlayer === null) return; + util.target.audioPlayer.setEffect(effect, soundState.effects[effect]); + } + + clearEffects (args, util) { + const soundState = this._getSoundState(util.target); + for (const effect in soundState.effects) { + if (!soundState.effects.hasOwnProperty(effect)) continue; + soundState.effects[effect] = 0; + } + if (util.target.audioPlayer === null) return; + util.target.audioPlayer.clearEffects(); + } + + setVolume (args, util) { + const volume = Cast.toNumber(args.VOLUME); + this._updateVolume(volume, util); + } + + changeVolume (args, util) { + const soundState = this._getSoundState(util.target); + const volume = Cast.toNumber(args.VOLUME) + soundState.volume; + this._updateVolume(volume, util); + } + + _updateVolume (volume, util) { + const soundState = this._getSoundState(util.target); + volume = MathUtil.clamp(volume, 0, 100); + soundState.volume = volume; + if (util.target.audioPlayer === null) return; + util.target.audioPlayer.setVolume(soundState.volume); + } + + getVolume (args, util) { + const soundState = this._getSoundState(util.target); + return soundState.volume; + } + + setTempo (args) { + const tempo = Cast.toNumber(args.TEMPO); + this._updateTempo(tempo); + } + + changeTempo (args) { + const change = Cast.toNumber(args.TEMPO); + if (typeof this.runtime.audioEngine === 'undefined') return; + const tempo = change + this.runtime.audioEngine.currentTempo; + this._updateTempo(tempo); + } + + _updateTempo (tempo) { + tempo = MathUtil.clamp(tempo, Scratch3SoundBlocks.TEMPO_RANGE.min, Scratch3SoundBlocks.TEMPO_RANGE.max); + if (typeof this.runtime.audioEngine === 'undefined') return; + this.runtime.audioEngine.setTempo(tempo); + } + + getTempo () { + if (typeof this.runtime.audioEngine === 'undefined') return; + return this.runtime.audioEngine.currentTempo; + } + + soundsMenu (args) { + return args.SOUND_MENU; + } + + beatsMenu (args) { + return args.BEATS; + } + + effectsMenu (args) { + return args.EFFECT; + } +} module.exports = Scratch3SoundBlocks; diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 6d2fa839..0b4b1ec4 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -8,490 +8,494 @@ const xmlEscape = require('../util/xml-escape'); * and handle updates from Scratch Blocks events. */ -const Blocks = function () { - /** - * All blocks in the workspace. - * Keys are block IDs, values are metadata about the block. - * @type {Object.} - */ - this._blocks = {}; +class Blocks { + constructor () { + /** + * All blocks in the workspace. + * Keys are block IDs, values are metadata about the block. + * @type {Object.} + */ + this._blocks = {}; + + /** + * All top-level scripts in the workspace. + * A list of block IDs that represent scripts (i.e., first block in script). + * @type {Array.} + */ + this._scripts = []; + } /** - * All top-level scripts in the workspace. - * A list of block IDs that represent scripts (i.e., first block in script). - * @type {Array.} + * Blockly inputs that represent statements/branch. + * are prefixed with this string. + * @const{string} */ - this._scripts = []; -}; - -/** - * Blockly inputs that represent statements/branch. - * are prefixed with this string. - * @const{string} - */ -Blocks.BRANCH_INPUT_PREFIX = 'SUBSTACK'; - -/** - * Provide an object with metadata for the requested block ID. - * @param {!string} blockId ID of block we have stored. - * @return {?object} Metadata about the block, if it exists. - */ -Blocks.prototype.getBlock = function (blockId) { - return this._blocks[blockId]; -}; - -/** - * Get all known top-level blocks that start scripts. - * @return {Array.} List of block IDs. - */ -Blocks.prototype.getScripts = function () { - return this._scripts; -}; - - /** - * Get the next block for a particular block - * @param {?string} id ID of block to get the next block for - * @return {?string} ID of next block in the sequence - */ -Blocks.prototype.getNextBlock = function (id) { - const block = this._blocks[id]; - return (typeof block === 'undefined') ? null : block.next; -}; - -/** - * Get the branch for a particular C-shaped block. - * @param {?string} id ID for block to get the branch for. - * @param {?number} branchNum Which branch to select (e.g. for if-else). - * @return {?string} ID of block in the branch. - */ -Blocks.prototype.getBranch = function (id, branchNum) { - const block = this._blocks[id]; - if (typeof block === 'undefined') return null; - if (!branchNum) branchNum = 1; - - let inputName = Blocks.BRANCH_INPUT_PREFIX; - if (branchNum > 1) { - inputName += branchNum; + static get BRANCH_INPUT_PREFIX () { + return 'SUBSTACK'; } - // Empty C-block? - const input = block.inputs[inputName]; - return (typeof input === 'undefined') ? null : input.block; -}; - -/** - * Get the opcode for a particular block - * @param {?object} block The block to query - * @return {?string} the opcode corresponding to that block - */ -Blocks.prototype.getOpcode = function (block) { - return (typeof block === 'undefined') ? null : block.opcode; -}; - -/** - * Get all fields and their values for a block. - * @param {?object} block The block to query. - * @return {?object} All fields and their values. - */ -Blocks.prototype.getFields = function (block) { - return (typeof block === 'undefined') ? null : block.fields; -}; - -/** - * Get all non-branch inputs for a block. - * @param {?object} block the block to query. - * @return {!object} All non-branch inputs and their associated blocks. - */ -Blocks.prototype.getInputs = function (block) { - if (typeof block === 'undefined') return null; - const inputs = {}; - for (const input in block.inputs) { - // Ignore blocks prefixed with branch prefix. - if (input.substring(0, Blocks.BRANCH_INPUT_PREFIX.length) !== - Blocks.BRANCH_INPUT_PREFIX) { - inputs[input] = block.inputs[input]; - } + /** + * Provide an object with metadata for the requested block ID. + * @param {!string} blockId ID of block we have stored. + * @return {?object} Metadata about the block, if it exists. + */ + getBlock (blockId) { + return this._blocks[blockId]; } - return inputs; -}; -/** - * Get mutation data for a block. - * @param {?object} block The block to query. - * @return {?object} Mutation for the block. - */ -Blocks.prototype.getMutation = function (block) { - return (typeof block === 'undefined') ? null : block.mutation; -}; - -/** - * Get the top-level script for a given block. - * @param {?string} id ID of block to query. - * @return {?string} ID of top-level script block. - */ -Blocks.prototype.getTopLevelScript = function (id) { - let block = this._blocks[id]; - if (typeof block === 'undefined') return null; - while (block.parent !== null) { - block = this._blocks[block.parent]; + /** + * Get all known top-level blocks that start scripts. + * @return {Array.} List of block IDs. + */ + getScripts () { + return this._scripts; } - return block.id; -}; -/** - * Get the procedure definition for a given name. - * @param {?string} name Name of procedure to query. - * @return {?string} ID of procedure definition. - */ -Blocks.prototype.getProcedureDefinition = function (name) { - for (const id in this._blocks) { - if (!this._blocks.hasOwnProperty(id)) continue; + /** + * Get the next block for a particular block + * @param {?string} id ID of block to get the next block for + * @return {?string} ID of next block in the sequence + */ + getNextBlock (id) { const block = this._blocks[id]; - if ((block.opcode === 'procedures_defnoreturn' || - block.opcode === 'procedures_defreturn') && - block.mutation.proccode === name) { - return id; - } + return (typeof block === 'undefined') ? null : block.next; } - 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 (const id in this._blocks) { - if (!this._blocks.hasOwnProperty(id)) continue; + /** + * Get the branch for a particular C-shaped block. + * @param {?string} id ID for block to get the branch for. + * @param {?number} branchNum Which branch to select (e.g. for if-else). + * @return {?string} ID of block in the branch. + */ + getBranch (id, branchNum) { const block = this._blocks[id]; - if ((block.opcode === 'procedures_defnoreturn' || - block.opcode === 'procedures_defreturn') && - block.mutation.proccode === name) { - return JSON.parse(block.mutation.argumentnames); + if (typeof block === 'undefined') return null; + if (!branchNum) branchNum = 1; + + let inputName = Blocks.BRANCH_INPUT_PREFIX; + if (branchNum > 1) { + inputName += branchNum; } - } - 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 {?Runtime} optRuntime Optional runtime to forward click events to. - */ - -Blocks.prototype.blocklyListen = function (e, optRuntime) { - // Validate event - if (typeof e !== 'object') return; - if (typeof e.blockId !== 'string') return; - - // UI event: clicked scripts toggle in the runtime. - if (e.element === 'stackclick') { - if (optRuntime) { - optRuntime.toggleScript(e.blockId); - } - return; + // Empty C-block? + const input = block.inputs[inputName]; + return (typeof input === 'undefined') ? null : input.block; } - // Block create/update/destroy - switch (e.type) { - case 'create': { - const newBlocks = adapter(e); - // A create event can create many blocks. Add them all. - for (let i = 0; i < newBlocks.length; i++) { - this.createBlock(newBlocks[i]); - } - break; + /** + * Get the opcode for a particular block + * @param {?object} block The block to query + * @return {?string} the opcode corresponding to that block + */ + getOpcode (block) { + return (typeof block === 'undefined') ? null : block.opcode; } - case 'change': - this.changeBlock({ - id: e.blockId, - element: e.element, - name: e.name, - value: e.newValue - }); - break; - case 'move': - this.moveBlock({ - id: e.blockId, - oldParent: e.oldParentId, - oldInput: e.oldInputName, - newParent: e.newParentId, - newInput: e.newInputName, - newCoordinate: e.newCoordinate - }); - break; - case 'delete': - // Don't accept delete events for missing blocks, - // or shadow blocks being obscured. - if (!this._blocks.hasOwnProperty(e.blockId) || - this._blocks[e.blockId].shadow) { + + /** + * Get all fields and their values for a block. + * @param {?object} block The block to query. + * @return {?object} All fields and their values. + */ + getFields (block) { + return (typeof block === 'undefined') ? null : block.fields; + } + + /** + * Get all non-branch inputs for a block. + * @param {?object} block the block to query. + * @return {!object} All non-branch inputs and their associated blocks. + */ + getInputs (block) { + if (typeof block === 'undefined') return null; + const inputs = {}; + for (const input in block.inputs) { + // Ignore blocks prefixed with branch prefix. + if (input.substring(0, Blocks.BRANCH_INPUT_PREFIX.length) !== + Blocks.BRANCH_INPUT_PREFIX) { + inputs[input] = block.inputs[input]; + } + } + return inputs; + } + + /** + * Get mutation data for a block. + * @param {?object} block The block to query. + * @return {?object} Mutation for the block. + */ + getMutation (block) { + return (typeof block === 'undefined') ? null : block.mutation; + } + + /** + * Get the top-level script for a given block. + * @param {?string} id ID of block to query. + * @return {?string} ID of top-level script block. + */ + getTopLevelScript (id) { + let block = this._blocks[id]; + if (typeof block === 'undefined') return null; + while (block.parent !== null) { + block = this._blocks[block.parent]; + } + return block.id; + } + + /** + * Get the procedure definition for a given name. + * @param {?string} name Name of procedure to query. + * @return {?string} ID of procedure definition. + */ + getProcedureDefinition (name) { + for (const id in this._blocks) { + if (!this._blocks.hasOwnProperty(id)) continue; + const block = this._blocks[id]; + if ((block.opcode === 'procedures_defnoreturn' || + block.opcode === 'procedures_defreturn') && + 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. + */ + getProcedureParamNames (name) { + for (const id in this._blocks) { + if (!this._blocks.hasOwnProperty(id)) continue; + const 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 {?Runtime} optRuntime Optional runtime to forward click events to. + */ + + blocklyListen (e, optRuntime) { + // Validate event + if (typeof e !== 'object') return; + if (typeof e.blockId !== 'string') return; + + // UI event: clicked scripts toggle in the runtime. + if (e.element === 'stackclick') { + if (optRuntime) { + optRuntime.toggleScript(e.blockId); + } return; } - // Inform any runtime to forget about glows on this script. - if (optRuntime && this._blocks[e.blockId].topLevel) { - optRuntime.quietGlow(e.blockId); + + // Block create/update/destroy + switch (e.type) { + case 'create': { + const newBlocks = adapter(e); + // A create event can create many blocks. Add them all. + for (let i = 0; i < newBlocks.length; i++) { + this.createBlock(newBlocks[i]); + } + break; } - this.deleteBlock({ - id: e.blockId - }); - break; - } -}; - -// --------------------------------------------------------------------- - -/** - * Block management: create blocks and scripts from a `create` event - * @param {!object} block Blockly create event to be processed - */ -Blocks.prototype.createBlock = function (block) { - // Does the block already exist? - // Could happen, e.g., for an unobscured shadow. - if (this._blocks.hasOwnProperty(block.id)) { - return; - } - // Create new block. - 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). - if (block.topLevel) { - this._addScript(block.id); - } -}; - -/** - * Block management: change block field values - * @param {!object} args Blockly change event to be processed - */ -Blocks.prototype.changeBlock = function (args) { - // Validate - if (args.element !== 'field' && args.element !== 'mutation') return; - const block = this._blocks[args.id]; - if (typeof block === 'undefined') return; - - if (args.element === 'field') { - // Update block value - if (!block.fields[args.name]) return; - block.fields[args.name].value = args.value; - } else if (args.element === 'mutation') { - block.mutation = mutationAdapter(args.value); - } -}; - -/** - * Block management: move blocks from parent to parent - * @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; - this._blocks[e.id].y = e.newCoordinate.y; - } - - // Remove from any old parent. - if (typeof e.oldParent !== 'undefined') { - const oldParent = this._blocks[e.oldParent]; - if (typeof e.oldInput !== 'undefined' && - oldParent.inputs[e.oldInput].block === e.id) { - // This block was connected to the old parent's input. - oldParent.inputs[e.oldInput].block = null; - } else if (oldParent.next === e.id) { - // This block was connected to the old parent's next connection. - oldParent.next = null; + case 'change': + this.changeBlock({ + id: e.blockId, + element: e.element, + name: e.name, + value: e.newValue + }); + break; + case 'move': + this.moveBlock({ + id: e.blockId, + oldParent: e.oldParentId, + oldInput: e.oldInputName, + newParent: e.newParentId, + newInput: e.newInputName, + newCoordinate: e.newCoordinate + }); + break; + case 'delete': + // 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. + if (optRuntime && this._blocks[e.blockId].topLevel) { + optRuntime.quietGlow(e.blockId); + } + this.deleteBlock({ + id: e.blockId + }); + break; } - this._blocks[e.id].parent = null; } - // Has the block become a top-level block? - if (typeof e.newParent === 'undefined') { - this._addScript(e.id); - } else { - // Remove script, if one exists. - this._deleteScript(e.id); - // Otherwise, try to connect it in its new place. - if (typeof e.newInput === 'undefined') { - // Moved to the new parent's next connection. - this._blocks[e.newParent].next = e.id; + // --------------------------------------------------------------------- + + /** + * Block management: create blocks and scripts from a `create` event + * @param {!object} block Blockly create event to be processed + */ + createBlock (block) { + // Does the block already exist? + // Could happen, e.g., for an unobscured shadow. + if (this._blocks.hasOwnProperty(block.id)) { + return; + } + // Create new block. + 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). + if (block.topLevel) { + this._addScript(block.id); + } + } + + /** + * Block management: change block field values + * @param {!object} args Blockly change event to be processed + */ + changeBlock (args) { + // Validate + if (args.element !== 'field' && args.element !== 'mutation') return; + const block = this._blocks[args.id]; + if (typeof block === 'undefined') return; + + if (args.element === 'field') { + // Update block value + if (!block.fields[args.name]) return; + block.fields[args.name].value = args.value; + } else if (args.element === 'mutation') { + block.mutation = mutationAdapter(args.value); + } + } + + /** + * Block management: move blocks from parent to parent + * @param {!object} e Blockly move event to be processed + */ + moveBlock (e) { + if (!this._blocks.hasOwnProperty(e.id)) { + return; + } + + // Move coordinate changes. + if (e.newCoordinate) { + this._blocks[e.id].x = e.newCoordinate.x; + this._blocks[e.id].y = e.newCoordinate.y; + } + + // Remove from any old parent. + if (typeof e.oldParent !== 'undefined') { + const oldParent = this._blocks[e.oldParent]; + if (typeof e.oldInput !== 'undefined' && + oldParent.inputs[e.oldInput].block === e.id) { + // This block was connected to the old parent's input. + oldParent.inputs[e.oldInput].block = null; + } else if (oldParent.next === e.id) { + // This block was connected to the old parent's next connection. + oldParent.next = null; + } + this._blocks[e.id].parent = null; + } + + // Has the block become a top-level block? + if (typeof e.newParent === 'undefined') { + this._addScript(e.id); } else { - // Moved to the new parent's input. - // Don't obscure the shadow block. - let oldShadow = null; - if (this._blocks[e.newParent].inputs.hasOwnProperty(e.newInput)) { - oldShadow = this._blocks[e.newParent].inputs[e.newInput].shadow; + // Remove script, if one exists. + this._deleteScript(e.id); + // Otherwise, try to connect it in its new place. + if (typeof e.newInput === 'undefined') { + // Moved to the new parent's next connection. + this._blocks[e.newParent].next = e.id; + } else { + // Moved to the new parent's input. + // Don't obscure the shadow block. + let oldShadow = null; + if (this._blocks[e.newParent].inputs.hasOwnProperty(e.newInput)) { + oldShadow = this._blocks[e.newParent].inputs[e.newInput].shadow; + } + this._blocks[e.newParent].inputs[e.newInput] = { + name: e.newInput, + block: e.id, + shadow: oldShadow + }; } - this._blocks[e.newParent].inputs[e.newInput] = { - name: e.newInput, - block: e.id, - shadow: oldShadow - }; - } - this._blocks[e.id].parent = e.newParent; - } -}; - -/** - * Block management: delete blocks and their associated scripts. - * @param {!object} e Blockly delete event to be processed. - */ -Blocks.prototype.deleteBlock = function (e) { - // @todo In runtime, stop threads running on this script. - - // Get block - const block = this._blocks[e.id]; - - // Delete children - if (block.next !== null) { - this.deleteBlock({id: block.next}); - } - - // Delete inputs (including branches) - for (const input in block.inputs) { - // If it's null, the block in this input moved away. - if (block.inputs[input].block !== null) { - this.deleteBlock({id: block.inputs[input].block}); - } - // Delete obscured shadow blocks. - if (block.inputs[input].shadow !== null && - block.inputs[input].shadow !== block.inputs[input].block) { - this.deleteBlock({id: block.inputs[input].shadow}); + this._blocks[e.id].parent = e.newParent; } } - // Delete any script starting with this block. - this._deleteScript(e.id); + /** + * Block management: delete blocks and their associated scripts. + * @param {!object} e Blockly delete event to be processed. + */ + deleteBlock (e) { + // @todo In runtime, stop threads running on this script. - // Delete block itself. - delete this._blocks[e.id]; -}; + // Get block + const block = this._blocks[e.id]; -// --------------------------------------------------------------------- + // Delete children + if (block.next !== null) { + this.deleteBlock({id: block.next}); + } -/** - * Encode all of `this._blocks` as an XML string usable - * by a Blockly/scratch-blocks workspace. - * @return {string} String of XML representing this object's blocks. - */ -Blocks.prototype.toXML = function () { - let xmlString = ''; - for (let i = 0; i < this._scripts.length; i++) { - xmlString += this.blockToXML(this._scripts[i]); - } - return `${xmlString}`; -}; - -/** - * Recursively encode an individual block and its children - * into a Blockly/scratch-blocks XML string. - * @param {!string} blockId ID of block to encode. - * @return {string} String of XML representing this block and any children. - */ -Blocks.prototype.blockToXML = function (blockId) { - const block = this._blocks[blockId]; - // Encode properties of this block. - const tagName = (block.shadow) ? 'shadow' : 'block'; - const xy = (block.topLevel) ? - ` x="${block.x}" y="${block.y}"` : - ''; - let xmlString = ''; - xmlString += `<${tagName - } id="${block.id}"` + - ` type="${block.opcode}"${ - xy - }>`; - // Add any mutation. Must come before inputs. - if (block.mutation) { - xmlString += this.mutationToXML(block.mutation); - } - // Add any inputs on this block. - for (const input in block.inputs) { - if (!block.inputs.hasOwnProperty(input)) continue; - const blockInput = block.inputs[input]; - // Only encode a value tag if the value input is occupied. - if (blockInput.block || blockInput.shadow) { - xmlString += ``; - if (blockInput.block) { - xmlString += this.blockToXML(blockInput.block); + // Delete inputs (including branches) + for (const input in block.inputs) { + // If it's null, the block in this input moved away. + if (block.inputs[input].block !== null) { + this.deleteBlock({id: block.inputs[input].block}); } - if (blockInput.shadow && blockInput.shadow !== blockInput.block) { - // Obscured shadow. - xmlString += this.blockToXML(blockInput.shadow); + // Delete obscured shadow blocks. + if (block.inputs[input].shadow !== null && + block.inputs[input].shadow !== block.inputs[input].block) { + this.deleteBlock({id: block.inputs[input].shadow}); } - xmlString += ''; } + + // Delete any script starting with this block. + this._deleteScript(e.id); + + // Delete block itself. + delete this._blocks[e.id]; } - // Add any fields on this block. - for (const field in block.fields) { - if (!block.fields.hasOwnProperty(field)) continue; - const blockField = block.fields[field]; - let value = blockField.value; - if (typeof value === 'string') { - value = xmlEscape(blockField.value); + + // --------------------------------------------------------------------- + + /** + * Encode all of `this._blocks` as an XML string usable + * by a Blockly/scratch-blocks workspace. + * @return {string} String of XML representing this object's blocks. + */ + toXML () { + let xmlString = ''; + for (let i = 0; i < this._scripts.length; i++) { + xmlString += this.blockToXML(this._scripts[i]); } - xmlString += `${ - value}`; + return `${xmlString}`; } - // Add blocks connected to the next connection. - if (block.next) { - xmlString += `${this.blockToXML(block.next)}`; + + /** + * Recursively encode an individual block and its children + * into a Blockly/scratch-blocks XML string. + * @param {!string} blockId ID of block to encode. + * @return {string} String of XML representing this block and any children. + */ + blockToXML (blockId) { + const block = this._blocks[blockId]; + // Encode properties of this block. + const tagName = (block.shadow) ? 'shadow' : 'block'; + const xy = (block.topLevel) ? + ` x="${block.x}" y="${block.y}"` : + ''; + let xmlString = ''; + xmlString += `<${tagName + } id="${block.id}"` + + ` type="${block.opcode}"${ + xy + }>`; + // Add any mutation. Must come before inputs. + if (block.mutation) { + xmlString += this.mutationToXML(block.mutation); + } + // Add any inputs on this block. + for (const input in block.inputs) { + if (!block.inputs.hasOwnProperty(input)) continue; + const blockInput = block.inputs[input]; + // Only encode a value tag if the value input is occupied. + if (blockInput.block || blockInput.shadow) { + xmlString += ``; + if (blockInput.block) { + xmlString += this.blockToXML(blockInput.block); + } + if (blockInput.shadow && blockInput.shadow !== blockInput.block) { + // Obscured shadow. + xmlString += this.blockToXML(blockInput.shadow); + } + xmlString += ''; + } + } + // Add any fields on this block. + for (const field in block.fields) { + if (!block.fields.hasOwnProperty(field)) continue; + const blockField = block.fields[field]; + let value = blockField.value; + if (typeof value === 'string') { + value = xmlEscape(blockField.value); + } + xmlString += `${ + value}`; + } + // Add blocks connected to the next connection. + if (block.next) { + xmlString += `${this.blockToXML(block.next)}`; + } + xmlString += ``; + return xmlString; } - xmlString += ``; - return xmlString; -}; -/** - * Recursively encode a mutation object to XML. - * @param {!object} mutation Object representing a mutation. - * @return {string} XML string representing a mutation. - */ -Blocks.prototype.mutationToXML = function (mutation) { - let mutationString = `<${mutation.tagName}`; - for (const prop in mutation) { - if (prop === 'children' || prop === 'tagName') continue; - const mutationValue = (typeof mutation[prop] === 'string') ? - xmlEscape(mutation[prop]) : mutation[prop]; - mutationString += ` ${prop}="${mutationValue}"`; + /** + * Recursively encode a mutation object to XML. + * @param {!object} mutation Object representing a mutation. + * @return {string} XML string representing a mutation. + */ + mutationToXML (mutation) { + let mutationString = `<${mutation.tagName}`; + for (const prop in mutation) { + if (prop === 'children' || prop === 'tagName') continue; + const mutationValue = (typeof mutation[prop] === 'string') ? + xmlEscape(mutation[prop]) : mutation[prop]; + mutationString += ` ${prop}="${mutationValue}"`; + } + mutationString += '>'; + for (let i = 0; i < mutation.children.length; i++) { + mutationString += this.mutationToXML(mutation.children[i]); + } + mutationString += ``; + return mutationString; } - mutationString += '>'; - for (let i = 0; i < mutation.children.length; i++) { - mutationString += this.mutationToXML(mutation.children[i]); + + // --------------------------------------------------------------------- + + /** + * Helper to add a stack to `this._scripts`. + * @param {?string} topBlockId ID of block that starts the script. + */ + _addScript (topBlockId) { + const i = this._scripts.indexOf(topBlockId); + if (i > -1) return; // Already in scripts. + this._scripts.push(topBlockId); + // Update `topLevel` property on the top block. + this._blocks[topBlockId].topLevel = true; } - mutationString += ``; - return mutationString; -}; -// --------------------------------------------------------------------- - -/** - * Helper to add a stack to `this._scripts`. - * @param {?string} topBlockId ID of block that starts the script. - */ -Blocks.prototype._addScript = function (topBlockId) { - const i = this._scripts.indexOf(topBlockId); - if (i > -1) return; // Already in scripts. - this._scripts.push(topBlockId); - // Update `topLevel` property on the top block. - this._blocks[topBlockId].topLevel = true; -}; - -/** - * Helper to remove a script from `this._scripts`. - * @param {?string} topBlockId ID of block that starts the script. - */ -Blocks.prototype._deleteScript = function (topBlockId) { - const i = this._scripts.indexOf(topBlockId); - if (i > -1) this._scripts.splice(i, 1); - // Update `topLevel` property on the top block. - if (this._blocks[topBlockId]) this._blocks[topBlockId].topLevel = false; -}; + /** + * Helper to remove a script from `this._scripts`. + * @param {?string} topBlockId ID of block that starts the script. + */ + _deleteScript (topBlockId) { + const i = this._scripts.indexOf(topBlockId); + if (i > -1) this._scripts.splice(i, 1); + // Update `topLevel` property on the top block. + if (this._blocks[topBlockId]) this._blocks[topBlockId].topLevel = false; + } +} module.exports = Blocks; diff --git a/src/engine/list.js b/src/engine/list.js index c26213e2..bf6e8bc0 100644 --- a/src/engine/list.js +++ b/src/engine/list.js @@ -8,9 +8,11 @@ * @param {Array} contents Contents of the list, as an array. * @constructor */ -const List = function (name, contents) { - this.name = name; - this.contents = contents; -}; +class List { + constructor (name, contents) { + this.name = name; + this.contents = contents; + } +} module.exports = List; diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 6bde2ddf..f57a0e9f 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -2,7 +2,6 @@ const EventEmitter = require('events'); const Sequencer = require('./sequencer'); const Blocks = require('./blocks'); const Thread = require('./thread'); -const util = require('util'); // Virtual I/O devices. const Clock = require('../io/clock'); @@ -26,845 +25,867 @@ const defaultBlockPackages = { * Manages targets, scripts, and the sequencer. * @constructor */ -const Runtime = function () { - // Bind event emitter - EventEmitter.call(this); +class Runtime extends EventEmitter { + constructor () { + super(); + + /** + * Target management and storage. + * @type {Array.} + */ + this.targets = []; + + /** + * A list of threads that are currently running in the VM. + * Threads are added when execution starts and pruned when execution ends. + * @type {Array.} + */ + this.threads = []; + + /** @type {!Sequencer} */ + this.sequencer = new Sequencer(this); + + /** + * Storage container for flyout blocks. + * These will execute on `_editingTarget.` + * @type {!Blocks} + */ + this.flyoutBlocks = new Blocks(); + + /** + * Currently known editing target for the VM. + * @type {?Target} + */ + this._editingTarget = null; + + /** + * 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. + * @type {Object.} + */ + this._primitives = {}; + + /** + * Map to look up hat blocks' metadata. + * Keys are opcode for hat, values are metadata objects. + * @type {Object.} + */ + this._hats = {}; + + /** + * Currently known values for edge-activated hats. + * Keys are block ID for the hat; values are the currently known values. + * @type {Object.} + */ + this._edgeActivatedHatValues = {}; + + /** + * A list of script block IDs that were glowing during the previous frame. + * @type {!Array.} + */ + this._scriptGlowsPreviousFrame = []; + + /** + * Number of threads running during the previous frame + * @type {number} + */ + this._threadCount = 0; + + /** + * Currently known number of clones, used to enforce clone limit. + * @type {number} + */ + this._cloneCounter = 0; + + /** + * Whether the project is in "turbo mode." + * @type {Boolean} + */ + this.turboMode = false; + + /** + * Whether the project is in "compatibility mode" (30 TPS). + * @type {Boolean} + */ + this.compatibilityMode = false; + + /** + * A reference to the current runtime stepping interval, set + * by a `setInterval`. + * @type {!number} + */ + this._steppingInterval = null; + + /** + * Current length of a step. + * Changes as mode switches, and used by the sequencer to calculate + * WORK_TIME. + * @type {!number} + */ + this.currentStepTime = null; + + /** + * Whether any primitive has requested a redraw. + * Affects whether `Sequencer.stepThreads` will yield + * after stepping each thread. + * Reset on every frame. + * @type {boolean} + */ + this.redrawRequested = false; + + // Register all given block packages. + this._registerBlockPackages(); + + // Register and initialize "IO devices", containers for processing + // I/O related data. + /** @type {Object.} */ + this.ioDevices = { + clock: new Clock(), + keyboard: new Keyboard(this), + mouse: new Mouse(this) + }; + } /** - * Target management and storage. - * @type {Array.} + * Width of the stage, in pixels. + * @const {number} */ - this.targets = []; + static get STAGE_WIDTH () { + return 480; + } /** - * A list of threads that are currently running in the VM. - * Threads are added when execution starts and pruned when execution ends. - * @type {Array.} + * Height of the stage, in pixels. + * @const {number} */ - this.threads = []; - - /** @type {!Sequencer} */ - this.sequencer = new Sequencer(this); + static get STAGE_HEIGHT () { + return 360; + } /** - * Storage container for flyout blocks. - * These will execute on `_editingTarget.` - * @type {!Blocks} + * Event name for glowing a script. + * @const {string} */ - this.flyoutBlocks = new Blocks(); + static get SCRIPT_GLOW_ON () { + return 'SCRIPT_GLOW_ON'; + } /** - * Currently known editing target for the VM. - * @type {?Target} + * Event name for unglowing a script. + * @const {string} */ - this._editingTarget = null; + static get SCRIPT_GLOW_OFF () { + return 'SCRIPT_GLOW_OFF'; + } /** - * 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. - * @type {Object.} + * Event name for glowing a block. + * @const {string} */ - this._primitives = {}; + static get BLOCK_GLOW_ON () { + return 'BLOCK_GLOW_ON'; + } /** - * Map to look up hat blocks' metadata. - * Keys are opcode for hat, values are metadata objects. - * @type {Object.} + * Event name for unglowing a block. + * @const {string} */ - this._hats = {}; + static get BLOCK_GLOW_OFF () { + return 'BLOCK_GLOW_OFF'; + } /** - * Currently known values for edge-activated hats. - * Keys are block ID for the hat; values are the currently known values. - * @type {Object.} + * Event name for glowing the green flag + * @const {string} */ - this._edgeActivatedHatValues = {}; + static get PROJECT_RUN_START () { + return 'PROJECT_RUN_START'; + } /** - * A list of script block IDs that were glowing during the previous frame. - * @type {!Array.} + * Event name for unglowing the green flag + * @const {string} */ - this._scriptGlowsPreviousFrame = []; + static get PROJECT_RUN_STOP () { + return 'PROJECT_RUN_STOP'; + } /** - * Number of threads running during the previous frame - * @type {number} + * Event name for visual value report. + * @const {string} */ - this._threadCount = 0; + static get VISUAL_REPORT () { + return 'VISUAL_REPORT'; + } /** - * Currently known number of clones, used to enforce clone limit. - * @type {number} + * Event name for sprite info report. + * @const {string} */ - this._cloneCounter = 0; + static get SPRITE_INFO_REPORT () { + return 'SPRITE_INFO_REPORT'; + } /** - * Whether the project is in "turbo mode." - * @type {Boolean} + * How rapidly we try to step threads by default, in ms. */ - this.turboMode = false; + static get THREAD_STEP_INTERVAL () { + return 1000 / 60; + } /** - * Whether the project is in "compatibility mode" (30 TPS). - * @type {Boolean} + * In compatibility mode, how rapidly we try to step threads, in ms. */ - this.compatibilityMode = false; + static get THREAD_STEP_INTERVAL_COMPATIBILITY () { + return 1000 / 30; + } /** - * A reference to the current runtime stepping interval, set - * by a `setInterval`. - * @type {!number} + * How many clones can be created at a time. + * @const {number} */ - this._steppingInterval = null; + static get MAX_CLONES () { + return 300; + } + + // ----------------------------------------------------------------------------- + // ----------------------------------------------------------------------------- /** - * Current length of a step. - * Changes as mode switches, and used by the sequencer to calculate - * WORK_TIME. - * @type {!number} + * Register default block packages with this runtime. + * @todo Prefix opcodes with package name. + * @private */ - this.currentStepTime = null; - - /** - * Whether any primitive has requested a redraw. - * Affects whether `Sequencer.stepThreads` will yield - * after stepping each thread. - * Reset on every frame. - * @type {boolean} - */ - this.redrawRequested = false; - - // Register all given block packages. - this._registerBlockPackages(); - - // Register and initialize "IO devices", containers for processing - // I/O related data. - /** @type {Object.} */ - this.ioDevices = { - clock: new Clock(), - keyboard: new Keyboard(this), - mouse: new Mouse(this) - }; -}; - -/** - * Inherit from EventEmitter - */ -util.inherits(Runtime, EventEmitter); - -/** - * Width of the stage, in pixels. - * @const {number} - */ -Runtime.STAGE_WIDTH = 480; - -/** - * Height of the stage, in pixels. - * @const {number} - */ -Runtime.STAGE_HEIGHT = 360; - -/** - * Event name for glowing a script. - * @const {string} - */ -Runtime.SCRIPT_GLOW_ON = 'SCRIPT_GLOW_ON'; - -/** - * Event name for unglowing a script. - * @const {string} - */ -Runtime.SCRIPT_GLOW_OFF = 'SCRIPT_GLOW_OFF'; - -/** - * Event name for glowing a block. - * @const {string} - */ -Runtime.BLOCK_GLOW_ON = 'BLOCK_GLOW_ON'; - -/** - * Event name for unglowing a block. - * @const {string} - */ -Runtime.BLOCK_GLOW_OFF = 'BLOCK_GLOW_OFF'; - -/** - * Event name for glowing the green flag - * @const {string} - */ -Runtime.PROJECT_RUN_START = 'PROJECT_RUN_START'; - -/** - * Event name for unglowing the green flag - * @const {string} - */ -Runtime.PROJECT_RUN_STOP = 'PROJECT_RUN_STOP'; - -/** - * Event name for visual value report. - * @const {string} - */ -Runtime.VISUAL_REPORT = 'VISUAL_REPORT'; - -/** - * Event name for sprite info report. - * @const {string} - */ -Runtime.SPRITE_INFO_REPORT = 'SPRITE_INFO_REPORT'; - -/** - * How rapidly we try to step threads by default, in ms. - */ -Runtime.THREAD_STEP_INTERVAL = 1000 / 60; - -/** - * In compatibility mode, how rapidly we try to step threads, in ms. - */ -Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY = 1000 / 30; - -/** - * How many clones can be created at a time. - * @const {number} - */ -Runtime.MAX_CLONES = 300; - -// ----------------------------------------------------------------------------- -// ----------------------------------------------------------------------------- - -/** - * Register default block packages with this runtime. - * @todo Prefix opcodes with package name. - * @private - */ -Runtime.prototype._registerBlockPackages = function () { - for (const packageName in defaultBlockPackages) { - if (defaultBlockPackages.hasOwnProperty(packageName)) { - // @todo pass a different runtime depending on package privilege? - const packageObject = new (defaultBlockPackages[packageName])(this); - // Collect primitives from package. - if (packageObject.getPrimitives) { - const packagePrimitives = packageObject.getPrimitives(); - for (const op in packagePrimitives) { - if (packagePrimitives.hasOwnProperty(op)) { - this._primitives[op] = - packagePrimitives[op].bind(packageObject); + _registerBlockPackages () { + for (const packageName in defaultBlockPackages) { + if (defaultBlockPackages.hasOwnProperty(packageName)) { + // @todo pass a different runtime depending on package privilege? + const packageObject = new (defaultBlockPackages[packageName])(this); + // Collect primitives from package. + if (packageObject.getPrimitives) { + const packagePrimitives = packageObject.getPrimitives(); + for (const op in packagePrimitives) { + if (packagePrimitives.hasOwnProperty(op)) { + this._primitives[op] = + packagePrimitives[op].bind(packageObject); + } } } - } - // Collect hat metadata from package. - if (packageObject.getHats) { - const packageHats = packageObject.getHats(); - for (const hatName in packageHats) { - if (packageHats.hasOwnProperty(hatName)) { - this._hats[hatName] = packageHats[hatName]; + // Collect hat metadata from package. + if (packageObject.getHats) { + const packageHats = packageObject.getHats(); + for (const hatName in packageHats) { + if (packageHats.hasOwnProperty(hatName)) { + this._hats[hatName] = packageHats[hatName]; + } } } } } } -}; -/** - * Retrieve the function associated with the given opcode. - * @param {!string} opcode The opcode to look up. - * @return {Function} The function which implements the opcode. - */ -Runtime.prototype.getOpcodeFunction = function (opcode) { - return this._primitives[opcode]; -}; - -/** - * Return whether an opcode represents a hat block. - * @param {!string} opcode The opcode to look up. - * @return {boolean} True if the op is known to be a hat. - */ -Runtime.prototype.getIsHat = function (opcode) { - return this._hats.hasOwnProperty(opcode); -}; - -/** - * Return whether an opcode represents an edge-activated hat block. - * @param {!string} opcode The opcode to look up. - * @return {boolean} True if the op is known to be a edge-activated hat. - */ -Runtime.prototype.getIsEdgeActivatedHat = function (opcode) { - return this._hats.hasOwnProperty(opcode) && - this._hats[opcode].edgeActivated; -}; - -/** - * Update an edge-activated hat block value. - * @param {!string} blockId ID of hat to store value for. - * @param {*} newValue Value to store for edge-activated hat. - * @return {*} The old value for the edge-activated hat. - */ -Runtime.prototype.updateEdgeActivatedValue = function (blockId, newValue) { - const oldValue = this._edgeActivatedHatValues[blockId]; - this._edgeActivatedHatValues[blockId] = newValue; - return oldValue; -}; - -/** - * Clear all edge-activaed hat values. - */ -Runtime.prototype.clearEdgeActivatedValues = function () { - this._edgeActivatedHatValues = {}; -}; - -/** - * Attach the audio engine - * @param {!AudioEngine} audioEngine The audio engine to attach - */ -Runtime.prototype.attachAudioEngine = function (audioEngine) { - this.audioEngine = audioEngine; -}; - -/** - * Attach the renderer - * @param {!RenderWebGL} renderer The renderer to attach - */ -Runtime.prototype.attachRenderer = function (renderer) { - this.renderer = renderer; -}; - -/** - * Attach the storage module - * @param {!ScratchStorage} storage The storage module to attach - */ -Runtime.prototype.attachStorage = function (storage) { - this.storage = storage; -}; - -// ----------------------------------------------------------------------------- -// ----------------------------------------------------------------------------- - -/** - * Create a thread and push it to the list of threads. - * @param {!string} id ID of block that starts the stack. - * @param {!Target} target Target to run thread on. - * @return {!Thread} The newly created thread. - */ -Runtime.prototype._pushThread = function (id, target) { - const thread = new Thread(id); - thread.target = target; - thread.pushStack(id); - this.threads.push(thread); - return thread; -}; - -/** - * Remove a thread from the list of threads. - * @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. - const i = this.threads.indexOf(thread); - if (i > -1) { - this.threads.splice(i, 1); + /** + * Retrieve the function associated with the given opcode. + * @param {!string} opcode The opcode to look up. + * @return {Function} The function which implements the opcode. + */ + getOpcodeFunction (opcode) { + return this._primitives[opcode]; } -}; -/** - * Restart a thread in place, maintaining its position in the list of threads. - * This is used by `startHats` to and is necessary to ensure 2.0-like execution order. - * Test project: https://scratch.mit.edu/projects/130183108/ - * @param {!Thread} thread Thread object to restart. - */ -Runtime.prototype._restartThread = function (thread) { - const newThread = new Thread(thread.topBlock); - newThread.target = thread.target; - newThread.pushStack(thread.topBlock); - const i = this.threads.indexOf(thread); - if (i > -1) { - this.threads[i] = newThread; - } else { + /** + * Return whether an opcode represents a hat block. + * @param {!string} opcode The opcode to look up. + * @return {boolean} True if the op is known to be a hat. + */ + getIsHat (opcode) { + return this._hats.hasOwnProperty(opcode); + } + + /** + * Return whether an opcode represents an edge-activated hat block. + * @param {!string} opcode The opcode to look up. + * @return {boolean} True if the op is known to be a edge-activated hat. + */ + getIsEdgeActivatedHat (opcode) { + return this._hats.hasOwnProperty(opcode) && + this._hats[opcode].edgeActivated; + } + + /** + * Update an edge-activated hat block value. + * @param {!string} blockId ID of hat to store value for. + * @param {*} newValue Value to store for edge-activated hat. + * @return {*} The old value for the edge-activated hat. + */ + updateEdgeActivatedValue (blockId, newValue) { + const oldValue = this._edgeActivatedHatValues[blockId]; + this._edgeActivatedHatValues[blockId] = newValue; + return oldValue; + } + + /** + * Clear all edge-activaed hat values. + */ + clearEdgeActivatedValues () { + this._edgeActivatedHatValues = {}; + } + + /** + * Attach the audio engine + * @param {!AudioEngine} audioEngine The audio engine to attach + */ + attachAudioEngine (audioEngine) { + this.audioEngine = audioEngine; + } + + /** + * Attach the renderer + * @param {!RenderWebGL} renderer The renderer to attach + */ + attachRenderer (renderer) { + this.renderer = renderer; + } + + /** + * Attach the storage module + * @param {!ScratchStorage} storage The storage module to attach + */ + attachStorage (storage) { + this.storage = storage; + } + + // ----------------------------------------------------------------------------- + // ----------------------------------------------------------------------------- + + /** + * Create a thread and push it to the list of threads. + * @param {!string} id ID of block that starts the stack. + * @param {!Target} target Target to run thread on. + * @return {!Thread} The newly created thread. + */ + _pushThread (id, target) { + const thread = new Thread(id); + thread.target = target; + thread.pushStack(id); this.threads.push(thread); + return thread; } -}; -/** - * Return whether a thread is currently active/running. - * @param {?Thread} thread Thread object to check. - * @return {boolean} True if the thread is active/running. - */ -Runtime.prototype.isActiveThread = function (thread) { - return this.threads.indexOf(thread) > -1; -}; + /** + * Remove a thread from the list of threads. + * @param {?Thread} thread Thread object to remove from actives + */ + _removeThread (thread) { + // Inform sequencer to stop executing that thread. + this.sequencer.retireThread(thread); + // Remove from the list. + const i = this.threads.indexOf(thread); + if (i > -1) { + this.threads.splice(i, 1); + } + } -/** - * Toggle a script. - * @param {!string} topBlockId ID of block that starts the script. - */ -Runtime.prototype.toggleScript = function (topBlockId) { - // Remove any existing thread. - for (let i = 0; i < this.threads.length; i++) { - if (this.threads[i].topBlock === topBlockId) { - this._removeThread(this.threads[i]); + /** + * Restart a thread in place, maintaining its position in the list of threads. + * This is used by `startHats` to and is necessary to ensure 2.0-like execution order. + * Test project: https://scratch.mit.edu/projects/130183108/ + * @param {!Thread} thread Thread object to restart. + */ + _restartThread (thread) { + const newThread = new Thread(thread.topBlock); + newThread.target = thread.target; + newThread.pushStack(thread.topBlock); + const i = this.threads.indexOf(thread); + if (i > -1) { + this.threads[i] = newThread; + } else { + this.threads.push(thread); + } + } + + /** + * Return whether a thread is currently active/running. + * @param {?Thread} thread Thread object to check. + * @return {boolean} True if the thread is active/running. + */ + isActiveThread (thread) { + return this.threads.indexOf(thread) > -1; + } + + /** + * Toggle a script. + * @param {!string} topBlockId ID of block that starts the script. + */ + toggleScript (topBlockId) { + // Remove any existing thread. + for (let i = 0; i < this.threads.length; i++) { + if (this.threads[i].topBlock === topBlockId) { + this._removeThread(this.threads[i]); + return; + } + } + // Otherwise add it. + this._pushThread(topBlockId, this._editingTarget); + } + + /** + * Run a function `f` for all scripts in a workspace. + * `f` will be called with two parameters: + * - the top block ID of the script. + * - the target that owns the script. + * @param {!Function} f Function to call for each script. + * @param {Target=} optTarget Optionally, a target to restrict to. + */ + allScriptsDo (f, optTarget) { + let targets = this.targets; + if (optTarget) { + targets = [optTarget]; + } + for (let t = targets.length - 1; t >= 0; t--) { + const target = targets[t]; + const scripts = target.blocks.getScripts(); + for (let j = 0; j < scripts.length; j++) { + const topBlockId = scripts[j]; + f(topBlockId, target); + } + } + } + + /** + * Start all relevant hats. + * @param {!string} requestedHatOpcode Opcode of hats to start. + * @param {object=} optMatchFields Optionally, fields to match on the hat. + * @param {Target=} optTarget Optionally, a target to restrict to. + * @return {Array.} List of threads started by this function. + */ + startHats (requestedHatOpcode, + optMatchFields, optTarget) { + if (!this._hats.hasOwnProperty(requestedHatOpcode)) { + // No known hat with this opcode. return; } - } - // Otherwise add it. - this._pushThread(topBlockId, this._editingTarget); -}; + const instance = this; + const newThreads = []; -/** - * Run a function `f` for all scripts in a workspace. - * `f` will be called with two parameters: - * - the top block ID of the script. - * - the target that owns the script. - * @param {!Function} f Function to call for each script. - * @param {Target=} optTarget Optionally, a target to restrict to. - */ -Runtime.prototype.allScriptsDo = function (f, optTarget) { - let targets = this.targets; - if (optTarget) { - targets = [optTarget]; - } - for (let t = targets.length - 1; t >= 0; t--) { - const target = targets[t]; - const scripts = target.blocks.getScripts(); - for (let j = 0; j < scripts.length; j++) { - const topBlockId = scripts[j]; - f(topBlockId, target); - } - } -}; - -/** - * Start all relevant hats. - * @param {!string} requestedHatOpcode Opcode of hats to start. - * @param {object=} optMatchFields Optionally, fields to match on the hat. - * @param {Target=} optTarget Optionally, a target to restrict to. - * @return {Array.} List of threads started by this function. - */ -Runtime.prototype.startHats = function (requestedHatOpcode, - optMatchFields, optTarget) { - if (!this._hats.hasOwnProperty(requestedHatOpcode)) { - // No known hat with this opcode. - return; - } - const instance = this; - const newThreads = []; - - for (const opts in optMatchFields) { - if (!optMatchFields.hasOwnProperty(opts)) continue; - optMatchFields[opts] = optMatchFields[opts].toUpperCase(); - } - - // Consider all scripts, looking for hats with opcode `requestedHatOpcode`. - this.allScriptsDo((topBlockId, target) => { - const blocks = target.blocks; - const block = blocks.getBlock(topBlockId); - const potentialHatOpcode = block.opcode; - if (potentialHatOpcode !== requestedHatOpcode) { - // Not the right hat. - return; + for (const opts in optMatchFields) { + if (!optMatchFields.hasOwnProperty(opts)) continue; + optMatchFields[opts] = optMatchFields[opts].toUpperCase(); } - // Match any requested fields. - // For example: ensures that broadcasts match. - // This needs to happen before the block is evaluated - // (i.e., before the predicate can be run) because "broadcast and wait" - // needs to have a precise collection of started threads. - let hatFields = blocks.getFields(block); + // Consider all scripts, looking for hats with opcode `requestedHatOpcode`. + this.allScriptsDo((topBlockId, target) => { + const blocks = target.blocks; + const block = blocks.getBlock(topBlockId); + const potentialHatOpcode = block.opcode; + if (potentialHatOpcode !== requestedHatOpcode) { + // Not the right hat. + return; + } - // If no fields are present, check inputs (horizontal blocks) - if (Object.keys(hatFields).length === 0) { - const hatInputs = blocks.getInputs(block); - for (const input in hatInputs) { - if (!hatInputs.hasOwnProperty(input)) continue; - const id = hatInputs[input].block; - const inpBlock = blocks.getBlock(id); - const fields = blocks.getFields(inpBlock); - hatFields = Object.assign(fields, hatFields); + // Match any requested fields. + // For example: ensures that broadcasts match. + // This needs to happen before the block is evaluated + // (i.e., before the predicate can be run) because "broadcast and wait" + // needs to have a precise collection of started threads. + let hatFields = blocks.getFields(block); + + // If no fields are present, check inputs (horizontal blocks) + if (Object.keys(hatFields).length === 0) { + const hatInputs = blocks.getInputs(block); + for (const input in hatInputs) { + if (!hatInputs.hasOwnProperty(input)) continue; + const id = hatInputs[input].block; + const inpBlock = blocks.getBlock(id); + const fields = blocks.getFields(inpBlock); + hatFields = Object.assign(fields, hatFields); + } + } + + if (optMatchFields) { + for (const matchField in optMatchFields) { + if (hatFields[matchField].value.toUpperCase() !== + optMatchFields[matchField]) { + // Field mismatch. + return; + } + } + } + + // Look up metadata for the relevant hat. + const hatMeta = instance._hats[requestedHatOpcode]; + if (hatMeta.restartExistingThreads) { + // If `restartExistingThreads` is true, we should stop + // any existing threads starting with the top block. + for (let i = 0; i < instance.threads.length; i++) { + if (instance.threads[i].topBlock === topBlockId && + instance.threads[i].target === target) { + instance._restartThread(instance.threads[i]); + return; + } + } + } else { + // If `restartExistingThreads` is false, we should + // give up if any threads with the top block are running. + for (let j = 0; j < instance.threads.length; j++) { + if (instance.threads[j].topBlock === topBlockId && + instance.threads[j].target === target) { + // Some thread is already running. + return; + } + } + } + // Start the thread with this top block. + newThreads.push(instance._pushThread(topBlockId, target)); + }, optTarget); + return newThreads; + } + + /** + * Dispose all targets. Return to clean state. + */ + dispose () { + this.stopAll(); + this.targets.map(this.disposeTarget, this); + } + + /** + * Dispose of a target. + * @param {!Target} disposingTarget Target to dispose of. + */ + disposeTarget (disposingTarget) { + this.targets = this.targets.filter(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=} optThreadException Optional thread to skip. + */ + stopForTarget (target, optThreadException) { + // Stop any threads on the target. + for (let i = 0; i < this.threads.length; i++) { + if (this.threads[i] === optThreadException) { + continue; + } + if (this.threads[i].target === target) { + this._removeThread(this.threads[i]); } } + } - if (optMatchFields) { - for (const matchField in optMatchFields) { - if (hatFields[matchField].value.toUpperCase() !== - optMatchFields[matchField]) { - // Field mismatch. - return; + /** + * Start all threads that start with the green flag. + */ + greenFlag () { + this.stopAll(); + this.ioDevices.clock.resetProjectTimer(); + this.clearEdgeActivatedValues(); + // Inform all targets of the green flag. + for (let i = 0; i < this.targets.length; i++) { + this.targets[i].onGreenFlag(); + } + this.startHats('event_whenflagclicked'); + } + + /** + * Stop "everything." + */ + stopAll () { + // Dispose all clones. + const newTargets = []; + for (let i = 0; i < this.targets.length; i++) { + this.targets[i].onStopAll(); + if (this.targets[i].hasOwnProperty('isOriginal') && + !this.targets[i].isOriginal) { + this.targets[i].dispose(); + } else { + newTargets.push(this.targets[i]); + } + } + this.targets = newTargets; + // Dispose all threads. + const threadsCopy = this.threads.slice(); + while (threadsCopy.length > 0) { + const poppedThread = threadsCopy.pop(); + this._removeThread(poppedThread); + } + } + + /** + * Repeatedly run `sequencer.stepThreads` and filter out + * inactive threads after each iteration. + */ + _step () { + // Find all edge-activated hats, and add them to threads to be evaluated. + for (const hatType in this._hats) { + if (!this._hats.hasOwnProperty(hatType)) continue; + const hat = this._hats[hatType]; + if (hat.edgeActivated) { + this.startHats(hatType); + } + } + this.redrawRequested = false; + const doneThreads = this.sequencer.stepThreads(); + this._updateGlows(doneThreads); + this._setThreadCount(this.threads.length + doneThreads.length); + if (this.renderer) { + // @todo: Only render when this.redrawRequested or clones rendered. + this.renderer.draw(); + } + } + + /** + * Set the current editing target known by the runtime. + * @param {!Target} editingTarget New editing target. + */ + setEditingTarget (editingTarget) { + this._editingTarget = editingTarget; + // Script glows must be cleared. + this._scriptGlowsPreviousFrame = []; + this._updateGlows(); + this.spriteInfoReport(editingTarget); + } + + /** + * Set whether we are in 30 TPS compatibility mode. + * @param {boolean} compatibilityModeOn True iff in compatibility mode. + */ + setCompatibilityMode (compatibilityModeOn) { + this.compatibilityMode = compatibilityModeOn; + if (this._steppingInterval) { + clearInterval(this._steppingInterval); + this.start(); + } + } + + /** + * Emit glows/glow clears for scripts after a single tick. + * Looks at `this.threads` and notices which have turned on/off new glows. + * @param {Array.=} optExtraThreads Optional list of inactive threads. + */ + _updateGlows (optExtraThreads) { + const searchThreads = []; + searchThreads.push.apply(searchThreads, this.threads); + if (optExtraThreads) { + searchThreads.push.apply(searchThreads, optExtraThreads); + } + // Set of scripts that request a glow this frame. + const requestedGlowsThisFrame = []; + // Final set of scripts glowing during this frame. + const finalScriptGlows = []; + // Find all scripts that should be glowing. + for (let i = 0; i < searchThreads.length; i++) { + const thread = searchThreads[i]; + const target = thread.target; + if (target === this._editingTarget) { + const blockForThread = thread.blockGlowInFrame; + if (thread.requestScriptGlowInFrame) { + let script = target.blocks.getTopLevelScript(blockForThread); + if (!script) { + // Attempt to find in flyout blocks. + script = this.flyoutBlocks.getTopLevelScript( + blockForThread + ); + } + if (script) { + requestedGlowsThisFrame.push(script); + } } } } - - // Look up metadata for the relevant hat. - const hatMeta = instance._hats[requestedHatOpcode]; - if (hatMeta.restartExistingThreads) { - // If `restartExistingThreads` is true, we should stop - // any existing threads starting with the top block. - for (let i = 0; i < instance.threads.length; i++) { - if (instance.threads[i].topBlock === topBlockId && - instance.threads[i].target === target) { - instance._restartThread(instance.threads[i]); - return; - } + // Compare to previous frame. + for (let j = 0; j < this._scriptGlowsPreviousFrame.length; j++) { + const previousFrameGlow = this._scriptGlowsPreviousFrame[j]; + if (requestedGlowsThisFrame.indexOf(previousFrameGlow) < 0) { + // Glow turned off. + this.glowScript(previousFrameGlow, false); + } else { + // Still glowing. + finalScriptGlows.push(previousFrameGlow); } + } + for (let k = 0; k < requestedGlowsThisFrame.length; k++) { + const currentFrameGlow = requestedGlowsThisFrame[k]; + if (this._scriptGlowsPreviousFrame.indexOf(currentFrameGlow) < 0) { + // Glow turned on. + this.glowScript(currentFrameGlow, true); + finalScriptGlows.push(currentFrameGlow); + } + } + this._scriptGlowsPreviousFrame = finalScriptGlows; + } + + /** + * Emit run start/stop after each tick. Emits when `this.threads.length` goes + * between non-zero and zero + * + * @param {number} threadCount The new threadCount + */ + _setThreadCount (threadCount) { + if (this._threadCount === 0 && threadCount > 0) { + this.emit(Runtime.PROJECT_RUN_START); + } + if (this._threadCount > 0 && threadCount === 0) { + this.emit(Runtime.PROJECT_RUN_STOP); + } + this._threadCount = threadCount; + } + + /** + * "Quiet" a script's glow: stop the VM from generating glow/unglow events + * about that script. Use when a script has just been deleted, but we may + * still be tracking glow data about it. + * @param {!string} scriptBlockId Id of top-level block in script to quiet. + */ + quietGlow (scriptBlockId) { + const index = this._scriptGlowsPreviousFrame.indexOf(scriptBlockId); + if (index > -1) { + this._scriptGlowsPreviousFrame.splice(index, 1); + } + } + + /** + * Emit feedback for block glowing (used in the sequencer). + * @param {?string} blockId ID for the block to update glow + * @param {boolean} isGlowing True to turn on glow; false to turn off. + */ + glowBlock (blockId, isGlowing) { + if (isGlowing) { + this.emit(Runtime.BLOCK_GLOW_ON, {id: blockId}); } else { - // If `restartExistingThreads` is false, we should - // give up if any threads with the top block are running. - for (let j = 0; j < instance.threads.length; j++) { - if (instance.threads[j].topBlock === topBlockId && - instance.threads[j].target === target) { - // Some thread is already running. - return; - } - } - } - // Start the thread with this top block. - newThreads.push(instance._pushThread(topBlockId, target)); - }, optTarget); - return newThreads; -}; - -/** - * Dispose all targets. Return to clean state. - */ -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(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=} optThreadException Optional thread to skip. - */ -Runtime.prototype.stopForTarget = function (target, optThreadException) { - // Stop any threads on the target. - for (let i = 0; i < this.threads.length; i++) { - if (this.threads[i] === optThreadException) { - continue; - } - if (this.threads[i].target === target) { - this._removeThread(this.threads[i]); + this.emit(Runtime.BLOCK_GLOW_OFF, {id: blockId}); } } -}; -/** - * Start all threads that start with the green flag. - */ -Runtime.prototype.greenFlag = function () { - this.stopAll(); - this.ioDevices.clock.resetProjectTimer(); - this.clearEdgeActivatedValues(); - // Inform all targets of the green flag. - for (let i = 0; i < this.targets.length; i++) { - this.targets[i].onGreenFlag(); - } - this.startHats('event_whenflagclicked'); -}; - -/** - * Stop "everything." - */ -Runtime.prototype.stopAll = function () { - // Dispose all clones. - const newTargets = []; - for (let i = 0; i < this.targets.length; i++) { - this.targets[i].onStopAll(); - if (this.targets[i].hasOwnProperty('isOriginal') && - !this.targets[i].isOriginal) { - this.targets[i].dispose(); + /** + * Emit feedback for script glowing. + * @param {?string} topBlockId ID for the top block to update glow + * @param {boolean} isGlowing True to turn on glow; false to turn off. + */ + glowScript (topBlockId, isGlowing) { + if (isGlowing) { + this.emit(Runtime.SCRIPT_GLOW_ON, {id: topBlockId}); } else { - newTargets.push(this.targets[i]); + this.emit(Runtime.SCRIPT_GLOW_OFF, {id: topBlockId}); } } - this.targets = newTargets; - // Dispose all threads. - const threadsCopy = this.threads.slice(); - while (threadsCopy.length > 0) { - const poppedThread = threadsCopy.pop(); - this._removeThread(poppedThread); - } -}; -/** - * Repeatedly run `sequencer.stepThreads` and filter out - * inactive threads after each iteration. - */ -Runtime.prototype._step = function () { - // Find all edge-activated hats, and add them to threads to be evaluated. - for (const hatType in this._hats) { - if (!this._hats.hasOwnProperty(hatType)) continue; - const hat = this._hats[hatType]; - if (hat.edgeActivated) { - this.startHats(hatType); - } + /** + * Emit value for reporter to show in the blocks. + * @param {string} blockId ID for the block. + * @param {string} value Value to show associated with the block. + */ + visualReport (blockId, value) { + this.emit(Runtime.VISUAL_REPORT, {id: blockId, value: String(value)}); } - this.redrawRequested = false; - const doneThreads = this.sequencer.stepThreads(); - this._updateGlows(doneThreads); - this._setThreadCount(this.threads.length + doneThreads.length); - if (this.renderer) { - // @todo: Only render when this.redrawRequested or clones rendered. - this.renderer.draw(); - } -}; -/** - * Set the current editing target known by the runtime. - * @param {!Target} editingTarget New editing target. - */ -Runtime.prototype.setEditingTarget = function (editingTarget) { - this._editingTarget = editingTarget; - // Script glows must be cleared. - this._scriptGlowsPreviousFrame = []; - this._updateGlows(); - this.spriteInfoReport(editingTarget); -}; + /** + * Emit a sprite info report if the provided target is the original sprite + * @param {!Target} target Target to report sprite info for. + */ + spriteInfoReport (target) { + if (!target.isOriginal) return; -/** - * Set whether we are in 30 TPS compatibility mode. - * @param {boolean} compatibilityModeOn True iff in compatibility mode. - */ -Runtime.prototype.setCompatibilityMode = function (compatibilityModeOn) { - this.compatibilityMode = compatibilityModeOn; - if (this._steppingInterval) { - clearInterval(this._steppingInterval); - this.start(); + this.emit(Runtime.SPRITE_INFO_REPORT, target.toJSON()); } -}; -/** - * Emit glows/glow clears for scripts after a single tick. - * Looks at `this.threads` and notices which have turned on/off new glows. - * @param {Array.=} optExtraThreads Optional list of inactive threads. - */ -Runtime.prototype._updateGlows = function (optExtraThreads) { - const searchThreads = []; - searchThreads.push.apply(searchThreads, this.threads); - if (optExtraThreads) { - searchThreads.push.apply(searchThreads, optExtraThreads); - } - // Set of scripts that request a glow this frame. - const requestedGlowsThisFrame = []; - // Final set of scripts glowing during this frame. - const finalScriptGlows = []; - // Find all scripts that should be glowing. - for (let i = 0; i < searchThreads.length; i++) { - const thread = searchThreads[i]; - const target = thread.target; - if (target === this._editingTarget) { - const blockForThread = thread.blockGlowInFrame; - if (thread.requestScriptGlowInFrame) { - let script = target.blocks.getTopLevelScript(blockForThread); - if (!script) { - // Attempt to find in flyout blocks. - script = this.flyoutBlocks.getTopLevelScript( - blockForThread - ); - } - if (script) { - requestedGlowsThisFrame.push(script); - } + /** + * Get a target by its id. + * @param {string} targetId Id of target to find. + * @return {?Target} The target, if found. + */ + getTargetById (targetId) { + for (let i = 0; i < this.targets.length; i++) { + const target = this.targets[i]; + if (target.id === targetId) { + return target; } } } - // Compare to previous frame. - for (let j = 0; j < this._scriptGlowsPreviousFrame.length; j++) { - const previousFrameGlow = this._scriptGlowsPreviousFrame[j]; - if (requestedGlowsThisFrame.indexOf(previousFrameGlow) < 0) { - // Glow turned off. - this.glowScript(previousFrameGlow, false); - } else { - // Still glowing. - finalScriptGlows.push(previousFrameGlow); + + /** + * Get the first original (non-clone-block-created) sprite given a name. + * @param {string} spriteName Name of sprite to look for. + * @return {?Target} Target representing a sprite of the given name. + */ + getSpriteTargetByName (spriteName) { + for (let i = 0; i < this.targets.length; i++) { + const target = this.targets[i]; + if (target.sprite && target.sprite.name === spriteName) { + return target; + } } } - for (let k = 0; k < requestedGlowsThisFrame.length; k++) { - const currentFrameGlow = requestedGlowsThisFrame[k]; - if (this._scriptGlowsPreviousFrame.indexOf(currentFrameGlow) < 0) { - // Glow turned on. - this.glowScript(currentFrameGlow, true); - finalScriptGlows.push(currentFrameGlow); + + /** + * Get a target by its drawable id. + * @param {number} drawableID drawable id of target to find + * @return {?Target} The target, if found + */ + getTargetByDrawableId (drawableID) { + for (let i = 0; i < this.targets.length; i++) { + const target = this.targets[i]; + if (target.drawableID === drawableID) return target; } } - this._scriptGlowsPreviousFrame = finalScriptGlows; -}; -/** - * Emit run start/stop after each tick. Emits when `this.threads.length` goes - * between non-zero and zero - * - * @param {number} threadCount The new threadCount - */ -Runtime.prototype._setThreadCount = function (threadCount) { - if (this._threadCount === 0 && threadCount > 0) { - this.emit(Runtime.PROJECT_RUN_START); + /** + * Update the clone counter to track how many clones are created. + * @param {number} changeAmount How many clones have been created/destroyed. + */ + changeCloneCounter (changeAmount) { + this._cloneCounter += changeAmount; } - if (this._threadCount > 0 && threadCount === 0) { - this.emit(Runtime.PROJECT_RUN_STOP); + + /** + * Return whether there are clones available. + * @return {boolean} True until the number of clones hits Runtime.MAX_CLONES. + */ + clonesAvailable () { + return this._cloneCounter < Runtime.MAX_CLONES; } - this._threadCount = threadCount; -}; -/** - * "Quiet" a script's glow: stop the VM from generating glow/unglow events - * about that script. Use when a script has just been deleted, but we may - * still be tracking glow data about it. - * @param {!string} scriptBlockId Id of top-level block in script to quiet. - */ -Runtime.prototype.quietGlow = function (scriptBlockId) { - const index = this._scriptGlowsPreviousFrame.indexOf(scriptBlockId); - if (index > -1) { - this._scriptGlowsPreviousFrame.splice(index, 1); - } -}; - -/** - * Emit feedback for block glowing (used in the sequencer). - * @param {?string} blockId ID for the block to update glow - * @param {boolean} isGlowing True to turn on glow; false to turn off. - */ -Runtime.prototype.glowBlock = function (blockId, isGlowing) { - if (isGlowing) { - this.emit(Runtime.BLOCK_GLOW_ON, {id: blockId}); - } else { - this.emit(Runtime.BLOCK_GLOW_OFF, {id: blockId}); - } -}; - -/** - * Emit feedback for script glowing. - * @param {?string} topBlockId ID for the top block to update glow - * @param {boolean} isGlowing True to turn on glow; false to turn off. - */ -Runtime.prototype.glowScript = function (topBlockId, isGlowing) { - if (isGlowing) { - this.emit(Runtime.SCRIPT_GLOW_ON, {id: topBlockId}); - } else { - this.emit(Runtime.SCRIPT_GLOW_OFF, {id: topBlockId}); - } -}; - -/** - * Emit value for reporter to show in the blocks. - * @param {string} blockId ID for the block. - * @param {string} value Value to show associated with the block. - */ -Runtime.prototype.visualReport = function (blockId, value) { - this.emit(Runtime.VISUAL_REPORT, {id: blockId, value: String(value)}); -}; - -/** - * Emit a sprite info report if the provided target is the original sprite - * @param {!Target} target Target to report sprite info for. - */ -Runtime.prototype.spriteInfoReport = function (target) { - if (!target.isOriginal) return; - - this.emit(Runtime.SPRITE_INFO_REPORT, target.toJSON()); -}; - -/** - * Get a target by its id. - * @param {string} targetId Id of target to find. - * @return {?Target} The target, if found. - */ -Runtime.prototype.getTargetById = function (targetId) { - for (let i = 0; i < this.targets.length; i++) { - const target = this.targets[i]; - if (target.id === targetId) { - return target; + /** + * Get a target representing the Scratch stage, if one exists. + * @return {?Target} The target, if found. + */ + getTargetForStage () { + for (let i = 0; i < this.targets.length; i++) { + const target = this.targets[i]; + if (target.isStage) { + return target; + } } } -}; -/** - * Get the first original (non-clone-block-created) sprite given a name. - * @param {string} spriteName Name of sprite to look for. - * @return {?Target} Target representing a sprite of the given name. - */ -Runtime.prototype.getSpriteTargetByName = function (spriteName) { - for (let i = 0; i < this.targets.length; i++) { - const target = this.targets[i]; - if (target.sprite && target.sprite.name === spriteName) { - return target; + /** + * Tell the runtime to request a redraw. + * Use after a clone/sprite has completed some visible operation on the stage. + */ + requestRedraw () { + this.redrawRequested = true; + } + + /** + * Set up timers to repeatedly step in a browser. + */ + start () { + let interval = Runtime.THREAD_STEP_INTERVAL; + if (this.compatibilityMode) { + interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY; } + this.currentStepTime = interval; + this._steppingInterval = setInterval(() => { + this._step(); + }, interval); } -}; - -/** - * Get a target by its drawable id. - * @param {number} drawableID drawable id of target to find - * @return {?Target} The target, if found - */ -Runtime.prototype.getTargetByDrawableId = function (drawableID) { - for (let i = 0; i < this.targets.length; i++) { - const target = this.targets[i]; - if (target.drawableID === drawableID) return target; - } -}; - -/** - * Update the clone counter to track how many clones are created. - * @param {number} changeAmount How many clones have been created/destroyed. - */ -Runtime.prototype.changeCloneCounter = function (changeAmount) { - this._cloneCounter += changeAmount; -}; - -/** - * Return whether there are clones available. - * @return {boolean} True until the number of clones hits Runtime.MAX_CLONES. - */ -Runtime.prototype.clonesAvailable = function () { - return this._cloneCounter < Runtime.MAX_CLONES; -}; - -/** - * Get a target representing the Scratch stage, if one exists. - * @return {?Target} The target, if found. - */ -Runtime.prototype.getTargetForStage = function () { - for (let i = 0; i < this.targets.length; i++) { - const target = this.targets[i]; - if (target.isStage) { - return target; - } - } -}; - -/** - * Tell the runtime to request a redraw. - * Use after a clone/sprite has completed some visible operation on the stage. - */ -Runtime.prototype.requestRedraw = function () { - this.redrawRequested = true; -}; - -/** - * Set up timers to repeatedly step in a browser. - */ -Runtime.prototype.start = function () { - let interval = Runtime.THREAD_STEP_INTERVAL; - if (this.compatibilityMode) { - interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY; - } - this.currentStepTime = interval; - this._steppingInterval = setInterval(() => { - this._step(); - }, interval); -}; +} module.exports = Runtime; diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js index bb6bbe4c..c8051c87 100644 --- a/src/engine/sequencer.js +++ b/src/engine/sequencer.js @@ -2,244 +2,248 @@ const Timer = require('../util/timer'); const Thread = require('./thread'); const execute = require('./execute.js'); -const Sequencer = function (runtime) { - /** - * A utility timer for timing thread sequencing. - * @type {!Timer} - */ - this.timer = new Timer(); +class Sequencer { + constructor (runtime) { + /** + * A utility timer for timing thread sequencing. + * @type {!Timer} + */ + this.timer = new Timer(); + + /** + * Reference to the runtime owning this sequencer. + * @type {!Runtime} + */ + this.runtime = runtime; + } /** - * Reference to the runtime owning this sequencer. - * @type {!Runtime} + * Time to run a warp-mode thread, in ms. + * @type {number} */ - this.runtime = runtime; -}; + static get WARP_TIME () { + return 500; + } -/** - * Time to run a warp-mode thread, in ms. - * @type {number} - */ -Sequencer.WARP_TIME = 500; - -/** - * Step through all threads in `this.runtime.threads`, running them in order. - * @return {Array.} List of inactive threads after stepping. - */ -Sequencer.prototype.stepThreads = function () { - // Work time is 75% of the thread stepping interval. - const WORK_TIME = 0.75 * this.runtime.currentStepTime; - // Start counting toward WORK_TIME. - this.timer.start(); - // Count of active threads. - let numActiveThreads = Infinity; - // Whether `stepThreads` has run through a full single tick. - let ranFirstTick = false; - const doneThreads = []; - // Conditions for continuing to stepping threads: - // 1. We must have threads in the list, and some must be active. - // 2. Time elapsed must be less than WORK_TIME. - // 3. Either turbo mode, or no redraw has been requested by a primitive. - while (this.runtime.threads.length > 0 && - numActiveThreads > 0 && - this.timer.timeElapsed() < WORK_TIME && - (this.runtime.turboMode || !this.runtime.redrawRequested)) { - numActiveThreads = 0; - // Attempt to run each thread one time. - for (let i = 0; i < this.runtime.threads.length; i++) { - const activeThread = this.runtime.threads[i]; - if (activeThread.stack.length === 0 || - activeThread.status === Thread.STATUS_DONE) { - // Finished with this thread. - if (doneThreads.indexOf(activeThread) < 0) { - doneThreads.push(activeThread); + /** + * Step through all threads in `this.runtime.threads`, running them in order. + * @return {Array.} List of inactive threads after stepping. + */ + stepThreads () { + // Work time is 75% of the thread stepping interval. + const WORK_TIME = 0.75 * this.runtime.currentStepTime; + // Start counting toward WORK_TIME. + this.timer.start(); + // Count of active threads. + let numActiveThreads = Infinity; + // Whether `stepThreads` has run through a full single tick. + let ranFirstTick = false; + const doneThreads = []; + // Conditions for continuing to stepping threads: + // 1. We must have threads in the list, and some must be active. + // 2. Time elapsed must be less than WORK_TIME. + // 3. Either turbo mode, or no redraw has been requested by a primitive. + while (this.runtime.threads.length > 0 && + numActiveThreads > 0 && + this.timer.timeElapsed() < WORK_TIME && + (this.runtime.turboMode || !this.runtime.redrawRequested)) { + numActiveThreads = 0; + // Attempt to run each thread one time. + for (let i = 0; i < this.runtime.threads.length; i++) { + const activeThread = this.runtime.threads[i]; + if (activeThread.stack.length === 0 || + activeThread.status === Thread.STATUS_DONE) { + // Finished with this thread. + if (doneThreads.indexOf(activeThread) < 0) { + doneThreads.push(activeThread); + } + continue; + } + if (activeThread.status === Thread.STATUS_YIELD_TICK && + !ranFirstTick) { + // Clear single-tick yield from the last call of `stepThreads`. + activeThread.status = Thread.STATUS_RUNNING; + } + if (activeThread.status === Thread.STATUS_RUNNING || + activeThread.status === Thread.STATUS_YIELD) { + // Normal-mode thread: step. + this.stepThread(activeThread); + activeThread.warpTimer = null; + } + if (activeThread.status === Thread.STATUS_RUNNING) { + numActiveThreads++; } - continue; - } - if (activeThread.status === Thread.STATUS_YIELD_TICK && - !ranFirstTick) { - // Clear single-tick yield from the last call of `stepThreads`. - activeThread.status = Thread.STATUS_RUNNING; - } - if (activeThread.status === Thread.STATUS_RUNNING || - activeThread.status === Thread.STATUS_YIELD) { - // Normal-mode thread: step. - this.stepThread(activeThread); - activeThread.warpTimer = null; - } - if (activeThread.status === Thread.STATUS_RUNNING) { - numActiveThreads++; } + // We successfully ticked once. Prevents running STATUS_YIELD_TICK + // threads on the next tick. + ranFirstTick = true; } - // We successfully ticked once. Prevents running STATUS_YIELD_TICK - // threads on the next tick. - ranFirstTick = true; + // Filter inactive threads from `this.runtime.threads`. + this.runtime.threads = this.runtime.threads.filter(thread => { + if (doneThreads.indexOf(thread) > -1) { + return false; + } + return true; + }); + return doneThreads; } - // Filter inactive threads from `this.runtime.threads`. - this.runtime.threads = this.runtime.threads.filter(thread => { - if (doneThreads.indexOf(thread) > -1) { - return false; - } - return true; - }); - return doneThreads; -}; -/** - * Step the requested thread for as long as necessary. - * @param {!Thread} thread Thread object to step. - */ -Sequencer.prototype.stepThread = function (thread) { - let currentBlockId = thread.peekStack(); - if (!currentBlockId) { - // A "null block" - empty branch. - thread.popStack(); - } - while (thread.peekStack()) { - let isWarpMode = thread.peekStackFrame().warpMode; - if (isWarpMode && !thread.warpTimer) { - // Initialize warp-mode timer if it hasn't been already. - // This will start counting the thread toward `Sequencer.WARP_TIME`. - thread.warpTimer = new Timer(); - thread.warpTimer.start(); - } - // Execute the current block. - // Save the current block ID to notice if we did control flow. - currentBlockId = thread.peekStack(); - execute(this, thread); - thread.blockGlowInFrame = currentBlockId; - // If the thread has yielded or is waiting, yield to other threads. - if (thread.status === Thread.STATUS_YIELD) { - // Mark as running for next iteration. - thread.status = Thread.STATUS_RUNNING; - // In warp mode, yielded blocks are re-executed immediately. - if (isWarpMode && - thread.warpTimer.timeElapsed() <= Sequencer.WARP_TIME) { - continue; - } - return; - } else if (thread.status === Thread.STATUS_PROMISE_WAIT) { - // A promise was returned by the primitive. Yield the thread - // until the promise resolves. Promise resolution should reset - // thread.status to Thread.STATUS_RUNNING. - return; - } - // If no control flow has happened, switch to next block. - if (thread.peekStack() === currentBlockId) { - thread.goToNextBlock(); - } - // If no next block has been found at this point, look on the stack. - while (!thread.peekStack()) { + /** + * Step the requested thread for as long as necessary. + * @param {!Thread} thread Thread object to step. + */ + stepThread (thread) { + let currentBlockId = thread.peekStack(); + if (!currentBlockId) { + // A "null block" - empty branch. thread.popStack(); - - if (thread.stack.length === 0) { - // No more stack to run! - thread.status = Thread.STATUS_DONE; + } + while (thread.peekStack()) { + let isWarpMode = thread.peekStackFrame().warpMode; + if (isWarpMode && !thread.warpTimer) { + // Initialize warp-mode timer if it hasn't been already. + // This will start counting the thread toward `Sequencer.WARP_TIME`. + thread.warpTimer = new Timer(); + thread.warpTimer.start(); + } + // Execute the current block. + // Save the current block ID to notice if we did control flow. + currentBlockId = thread.peekStack(); + execute(this, thread); + thread.blockGlowInFrame = currentBlockId; + // If the thread has yielded or is waiting, yield to other threads. + if (thread.status === Thread.STATUS_YIELD) { + // Mark as running for next iteration. + thread.status = Thread.STATUS_RUNNING; + // In warp mode, yielded blocks are re-executed immediately. + if (isWarpMode && + thread.warpTimer.timeElapsed() <= Sequencer.WARP_TIME) { + continue; + } + return; + } else if (thread.status === Thread.STATUS_PROMISE_WAIT) { + // A promise was returned by the primitive. Yield the thread + // until the promise resolves. Promise resolution should reset + // thread.status to Thread.STATUS_RUNNING. return; } + // If no control flow has happened, switch to next block. + if (thread.peekStack() === currentBlockId) { + thread.goToNextBlock(); + } + // If no next block has been found at this point, look on the stack. + while (!thread.peekStack()) { + thread.popStack(); - const stackFrame = thread.peekStackFrame(); - isWarpMode = stackFrame.warpMode; - - if (stackFrame.isLoop) { - // The current level of the stack is marked as a loop. - // Return to yield for the frame/tick in general. - // Unless we're in warp mode - then only return if the - // warp timer is up. - if (!isWarpMode || - thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) { - // Don't do anything to the stack, since loops need - // to be re-executed. + if (thread.stack.length === 0) { + // No more stack to run! + thread.status = Thread.STATUS_DONE; return; } - // Don't go to the next block for this level of the stack, - // since loops need to be re-executed. - continue; - - } else if (stackFrame.waitingReporter) { - // This level of the stack was waiting for a value. - // This means a reporter has just returned - so don't go - // to the next block for this level of the stack. - return; + + const stackFrame = thread.peekStackFrame(); + isWarpMode = stackFrame.warpMode; + + if (stackFrame.isLoop) { + // The current level of the stack is marked as a loop. + // Return to yield for the frame/tick in general. + // Unless we're in warp mode - then only return if the + // warp timer is up. + if (!isWarpMode || + thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) { + // Don't do anything to the stack, since loops need + // to be re-executed. + return; + } + // Don't go to the next block for this level of the stack, + // since loops need to be re-executed. + continue; + + } else if (stackFrame.waitingReporter) { + // This level of the stack was waiting for a value. + // This means a reporter has just returned - so don't go + // to the next block for this level of the stack. + return; + } + // Get next block of existing block on the stack. + thread.goToNextBlock(); } - // Get next block of existing block on the stack. - thread.goToNextBlock(); } } -}; -/** - * Step a thread into a block's branch. - * @param {!Thread} thread Thread object to step to branch. - * @param {number} branchNum Which branch to step to (i.e., 1, 2). - * @param {boolean} isLoop Whether this block is a loop. - */ -Sequencer.prototype.stepToBranch = function (thread, branchNum, isLoop) { - if (!branchNum) { - branchNum = 1; - } - const currentBlockId = thread.peekStack(); - const branchId = thread.target.blocks.getBranch( - currentBlockId, - branchNum - ); - thread.peekStackFrame().isLoop = isLoop; - if (branchId) { - // Push branch ID to the thread's stack. - thread.pushStack(branchId); - } else { - thread.pushStack(null); - } -}; - -/** - * Step a procedure. - * @param {!Thread} thread Thread object to step to procedure. - * @param {!string} procedureCode Procedure code of procedure to step to. - */ -Sequencer.prototype.stepToProcedure = function (thread, procedureCode) { - const definition = thread.target.blocks.getProcedureDefinition(procedureCode); - if (!definition) { - return; - } - // Check if the call is recursive. - // If so, set the thread to yield after pushing. - const isRecursive = thread.isRecursiveCall(procedureCode); - // To step to a procedure, we put its definition on the stack. - // Execution for the thread will proceed through the definition hat - // and on to the main definition of the procedure. - // When that set of blocks finishes executing, it will be popped - // from the stack by the sequencer, returning control to the caller. - thread.pushStack(definition); - // In known warp-mode threads, only yield when time is up. - if (thread.peekStackFrame().warpMode && - thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) { - thread.status = Thread.STATUS_YIELD; - } else { - // Look for warp-mode flag on definition, and set the thread - // to warp-mode if needed. - const definitionBlock = thread.target.blocks.getBlock(definition); - const doWarp = definitionBlock.mutation.warp; - if (doWarp) { - thread.peekStackFrame().warpMode = true; + /** + * Step a thread into a block's branch. + * @param {!Thread} thread Thread object to step to branch. + * @param {number} branchNum Which branch to step to (i.e., 1, 2). + * @param {boolean} isLoop Whether this block is a loop. + */ + stepToBranch (thread, branchNum, isLoop) { + if (!branchNum) { + branchNum = 1; + } + const currentBlockId = thread.peekStack(); + const branchId = thread.target.blocks.getBranch( + currentBlockId, + branchNum + ); + thread.peekStackFrame().isLoop = isLoop; + if (branchId) { + // Push branch ID to the thread's stack. + thread.pushStack(branchId); } else { - // In normal-mode threads, yield any time we have a recursive call. - if (isRecursive) { - thread.status = Thread.STATUS_YIELD; + thread.pushStack(null); + } + } + + /** + * Step a procedure. + * @param {!Thread} thread Thread object to step to procedure. + * @param {!string} procedureCode Procedure code of procedure to step to. + */ + stepToProcedure (thread, procedureCode) { + const definition = thread.target.blocks.getProcedureDefinition(procedureCode); + if (!definition) { + return; + } + // Check if the call is recursive. + // If so, set the thread to yield after pushing. + const isRecursive = thread.isRecursiveCall(procedureCode); + // To step to a procedure, we put its definition on the stack. + // Execution for the thread will proceed through the definition hat + // and on to the main definition of the procedure. + // When that set of blocks finishes executing, it will be popped + // from the stack by the sequencer, returning control to the caller. + thread.pushStack(definition); + // In known warp-mode threads, only yield when time is up. + if (thread.peekStackFrame().warpMode && + thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) { + thread.status = Thread.STATUS_YIELD; + } else { + // Look for warp-mode flag on definition, and set the thread + // to warp-mode if needed. + const definitionBlock = thread.target.blocks.getBlock(definition); + const doWarp = definitionBlock.mutation.warp; + if (doWarp) { + thread.peekStackFrame().warpMode = true; + } else { + // In normal-mode threads, yield any time we have a recursive call. + if (isRecursive) { + thread.status = Thread.STATUS_YIELD; + } } } } -}; -/** - * Retire a thread in the middle, without considering further blocks. - * @param {!Thread} thread Thread object to retire. - */ -Sequencer.prototype.retireThread = function (thread) { - thread.stack = []; - thread.stackFrame = []; - thread.requestScriptGlowInFrame = false; - thread.status = Thread.STATUS_DONE; -}; + /** + * Retire a thread in the middle, without considering further blocks. + * @param {!Thread} thread Thread object to retire. + */ + retireThread (thread) { + thread.stack = []; + thread.stackFrame = []; + thread.requestScriptGlowInFrame = false; + thread.status = Thread.STATUS_DONE; + } +} module.exports = Sequencer; diff --git a/src/engine/target.js b/src/engine/target.js index 0c5c9881..d72937f6 100644 --- a/src/engine/target.js +++ b/src/engine/target.js @@ -1,5 +1,4 @@ const EventEmitter = require('events'); -const util = require('util'); const Blocks = require('./blocks'); const Variable = require('../engine/variable'); @@ -16,143 +15,140 @@ const uid = require('../util/uid'); * @param {?Blocks} blocks Blocks instance for the blocks owned by this target. * @constructor */ -const Target = function (blocks) { - EventEmitter.call(this); +class Target extends EventEmitter { + constructor (blocks) { + super(); - if (!blocks) { - blocks = new Blocks(this); - } - /** - * A unique ID for this target. - * @type {string} - */ - this.id = uid(); - /** - * Blocks run as code for this target. - * @type {!Blocks} - */ - this.blocks = blocks; - /** - * Dictionary of variables and their values for this target. - * Key is the variable name. - * @type {Object.} - */ - this.variables = {}; - /** - * Dictionary of lists and their contents for this target. - * Key is the list name. - * @type {Object.} - */ - this.lists = {}; - /** - * Dictionary of custom state for this target. - * This can be used to store target-specific custom state for blocks which need it. - * TODO: do we want to persist this in SB3 files? - * @type {Object.} - */ - this._customState = {}; -}; - -/** - * Inherit from EventEmitter - */ -util.inherits(Target, EventEmitter); - -/** - * Called when the project receives a "green flag." - * @abstract - */ -Target.prototype.onGreenFlag = function () {}; - -/** - * Return a human-readable name for this target. - * Target implementations should override this. - * @abstract - * @returns {string} Human-readable name for the target. - */ -Target.prototype.getName = function () { - return this.id; -}; - -/** - * Look up a variable object, and create it if one doesn't exist. - * Search begins for local variables; then look for globals. - * @param {!string} name Name of the variable. - * @return {!Variable} Variable object. - */ -Target.prototype.lookupOrCreateVariable = function (name) { - // If we have a local copy, return it. - if (this.variables.hasOwnProperty(name)) { - return this.variables[name]; - } - // If the stage has a global copy, return it. - if (this.runtime && !this.isStage) { - const stage = this.runtime.getTargetForStage(); - if (stage.variables.hasOwnProperty(name)) { - return stage.variables[name]; + if (!blocks) { + blocks = new Blocks(this); } + /** + * A unique ID for this target. + * @type {string} + */ + this.id = uid(); + /** + * Blocks run as code for this target. + * @type {!Blocks} + */ + this.blocks = blocks; + /** + * Dictionary of variables and their values for this target. + * Key is the variable name. + * @type {Object.} + */ + this.variables = {}; + /** + * Dictionary of lists and their contents for this target. + * Key is the list name. + * @type {Object.} + */ + this.lists = {}; + /** + * Dictionary of custom state for this target. + * This can be used to store target-specific custom state for blocks which need it. + * TODO: do we want to persist this in SB3 files? + * @type {Object.} + */ + this._customState = {}; } - // No variable with this name exists - create it locally. - const newVariable = new Variable(name, 0, false); - this.variables[name] = newVariable; - return newVariable; -}; -/** -* Look up a list object for this target, and create it if one doesn't exist. -* Search begins for local lists; then look for globals. -* @param {!string} name Name of the list. -* @return {!List} List object. - */ -Target.prototype.lookupOrCreateList = function (name) { - // If we have a local copy, return it. - if (this.lists.hasOwnProperty(name)) { - return this.lists[name]; + /** + * Called when the project receives a "green flag." + * @abstract + */ + onGreenFlag () {} + + /** + * Return a human-readable name for this target. + * Target implementations should override this. + * @abstract + * @returns {string} Human-readable name for the target. + */ + getName () { + return this.id; } - // If the stage has a global copy, return it. - if (this.runtime && !this.isStage) { - const stage = this.runtime.getTargetForStage(); - if (stage.lists.hasOwnProperty(name)) { - return stage.lists[name]; + + /** + * Look up a variable object, and create it if one doesn't exist. + * Search begins for local variables; then look for globals. + * @param {!string} name Name of the variable. + * @return {!Variable} Variable object. + */ + lookupOrCreateVariable (name) { + // If we have a local copy, return it. + if (this.variables.hasOwnProperty(name)) { + return this.variables[name]; } + // If the stage has a global copy, return it. + if (this.runtime && !this.isStage) { + const stage = this.runtime.getTargetForStage(); + if (stage.variables.hasOwnProperty(name)) { + return stage.variables[name]; + } + } + // No variable with this name exists - create it locally. + const newVariable = new Variable(name, 0, false); + this.variables[name] = newVariable; + return newVariable; } - // No list with this name exists - create it locally. - const newList = new List(name, []); - this.lists[name] = newList; - return newList; -}; -/** - * Post/edit sprite info. - * @param {object} data An object with sprite info data to set. - * @abstract - */ -Target.prototype.postSpriteInfo = function () {}; + /** + * Look up a list object for this target, and create it if one doesn't exist. + * Search begins for local lists; then look for globals. + * @param {!string} name Name of the list. + * @return {!List} List object. + */ + lookupOrCreateList (name) { + // If we have a local copy, return it. + if (this.lists.hasOwnProperty(name)) { + return this.lists[name]; + } + // If the stage has a global copy, return it. + if (this.runtime && !this.isStage) { + const stage = this.runtime.getTargetForStage(); + if (stage.lists.hasOwnProperty(name)) { + return stage.lists[name]; + } + } + // No list with this name exists - create it locally. + const newList = new List(name, []); + this.lists[name] = newList; + return newList; + } -/** - * Retrieve custom state associated with this target and the provided state ID. - * @param {string} stateId - specify which piece of state to retrieve. - * @returns {*} the associated state, if any was found. - */ -Target.prototype.getCustomState = function (stateId) { - return this._customState[stateId]; -}; + /** + * Post/edit sprite info. + * @param {object} data An object with sprite info data to set. + * @abstract + */ + postSpriteInfo () {} -/** - * Store custom state associated with this target and the provided state ID. - * @param {string} stateId - specify which piece of state to store on this target. - * @param {*} newValue - the state value to store. - */ -Target.prototype.setCustomState = function (stateId, newValue) { - this._customState[stateId] = newValue; -}; + /** + * Retrieve custom state associated with this target and the provided state ID. + * @param {string} stateId - specify which piece of state to retrieve. + * @returns {*} the associated state, if any was found. + */ + getCustomState (stateId) { + return this._customState[stateId]; + } -/** - * Call to destroy a target. - * @abstract - */ -Target.prototype.dispose = function () { - this._customState = {}; -}; + /** + * Store custom state associated with this target and the provided state ID. + * @param {string} stateId - specify which piece of state to store on this target. + * @param {*} newValue - the state value to store. + */ + setCustomState (stateId, newValue) { + this._customState[stateId] = newValue; + } + + /** + * Call to destroy a target. + * @abstract + */ + dispose () { + this._customState = {}; + } +} module.exports = Target; diff --git a/src/engine/thread.js b/src/engine/thread.js index bedd132d..208ff1e9 100644 --- a/src/engine/thread.js +++ b/src/engine/thread.js @@ -3,264 +3,276 @@ * @param {?string} firstBlock First block to execute in the thread. * @constructor */ -const Thread = function (firstBlock) { - /** - * ID of top block of the thread - * @type {!string} - */ - this.topBlock = firstBlock; +class Thread { + constructor (firstBlock) { + /** + * ID of top block of the thread + * @type {!string} + */ + this.topBlock = firstBlock; - /** - * Stack for the thread. When the sequencer enters a control structure, - * the block is pushed onto the stack so we know where to exit. - * @type {Array.} - */ - this.stack = []; + /** + * Stack for the thread. When the sequencer enters a control structure, + * the block is pushed onto the stack so we know where to exit. + * @type {Array.} + */ + this.stack = []; - /** - * Stack frames for the thread. Store metadata for the executing blocks. - * @type {Array.} - */ - this.stackFrames = []; + /** + * Stack frames for the thread. Store metadata for the executing blocks. + * @type {Array.} + */ + this.stackFrames = []; - /** - * Status of the thread, one of three states (below) - * @type {number} - */ - this.status = 0; /* Thread.STATUS_RUNNING */ + /** + * Status of the thread, one of three states (below) + * @type {number} + */ + this.status = 0; /* Thread.STATUS_RUNNING */ - /** - * Target of this thread. - * @type {?Target} - */ - this.target = null; + /** + * Target of this thread. + * @type {?Target} + */ + this.target = null; - /** - * Whether the thread requests its script to glow during this frame. - * @type {boolean} - */ - this.requestScriptGlowInFrame = false; - - /** - * Which block ID should glow during this frame, if any. - * @type {?string} - */ - this.blockGlowInFrame = null; - - /** - * A timer for when the thread enters warp mode. - * Substitutes the sequencer's count toward WORK_TIME on a per-thread basis. - * @type {?Timer} - */ - this.warpTimer = null; -}; - -/** - * Thread status for initialized or running thread. - * This is the default state for a thread - execution should run normally, - * stepping from block to block. - * @const - */ -Thread.STATUS_RUNNING = 0; - -/** - * Threads are in this state when a primitive is waiting on a promise; - * execution is paused until the promise changes thread status. - * @const - */ -Thread.STATUS_PROMISE_WAIT = 1; - -/** - * Thread status for yield. - * @const - */ -Thread.STATUS_YIELD = 2; - -/** - * Thread status for a single-tick yield. This will be cleared when the - * thread is resumed. - * @const - */ -Thread.STATUS_YIELD_TICK = 3; - -/** - * Thread status for a finished/done thread. - * Thread is in this state when there are no more blocks to execute. - * @const - */ -Thread.STATUS_DONE = 4; - -/** - * Push stack and update stack frames appropriately. - * @param {string} blockId Block ID to push to stack. - */ -Thread.prototype.pushStack = function (blockId) { - this.stack.push(blockId); - // Push an empty stack frame, if we need one. - // Might not, if we just popped the stack. - if (this.stack.length > this.stackFrames.length) { - // Copy warp mode from any higher level. - let warpMode = false; - if (this.stackFrames.length > 0 && this.stackFrames[this.stackFrames.length - 1]) { - warpMode = this.stackFrames[this.stackFrames.length - 1].warpMode; - } - this.stackFrames.push({ - isLoop: false, // Whether this level of the stack is a loop. - warpMode: warpMode, // Whether this level is in warp mode. - reported: {}, // Collects reported input values. - waitingReporter: null, // Name of waiting reporter. - params: {}, // Procedure parameters. - executionContext: {} // A context passed to block implementations. - }); - } -}; - -/** - * Reset the stack frame for use by the next block. - * (avoids popping and re-pushing a new stack frame - keeps the warpmode the same - * @param {string} blockId Block ID to push to stack. - */ -Thread.prototype.reuseStackForNextBlock = function (blockId) { - this.stack[this.stack.length - 1] = blockId; - const frame = this.stackFrames[this.stackFrames.length - 1]; - frame.isLoop = false; - // frame.warpMode = warpMode; // warp mode stays the same when reusing the stack frame. - frame.reported = {}; - frame.waitingReporter = null; - frame.params = {}; - frame.executionContext = {}; -}; - -/** - * Pop last block on the stack and its stack frame. - * @return {string} Block ID popped from the stack. - */ -Thread.prototype.popStack = function () { - this.stackFrames.pop(); - return this.stack.pop(); -}; - -/** - * Pop back down the stack frame until we hit a procedure call or the stack frame is emptied - */ -Thread.prototype.stopThisScript = function () { - let blockID = this.peekStack(); - while (blockID !== null) { - const block = this.target.blocks.getBlock(blockID); - if (typeof block !== 'undefined' && block.opcode === 'procedures_callnoreturn') { - break; - } - this.popStack(); - blockID = this.peekStack(); - } - - if (this.stack.length === 0) { - // Clean up! + /** + * Whether the thread requests its script to glow during this frame. + * @type {boolean} + */ this.requestScriptGlowInFrame = false; - this.status = Thread.STATUS_DONE; + + /** + * Which block ID should glow during this frame, if any. + * @type {?string} + */ + this.blockGlowInFrame = null; + + /** + * A timer for when the thread enters warp mode. + * Substitutes the sequencer's count toward WORK_TIME on a per-thread basis. + * @type {?Timer} + */ + this.warpTimer = null; } -}; -/** - * Get top stack item. - * @return {?string} Block ID on top of stack. - */ -Thread.prototype.peekStack = function () { - return this.stack.length > 0 ? this.stack[this.stack.length - 1] : null; -}; - - -/** - * Get top stack frame. - * @return {?object} Last stack frame stored on this thread. - */ -Thread.prototype.peekStackFrame = function () { - return this.stackFrames.length > 0 ? this.stackFrames[this.stackFrames.length - 1] : null; -}; - -/** - * Get stack frame above the current top. - * @return {?object} Second to last stack frame stored on this thread. - */ -Thread.prototype.peekParentStackFrame = function () { - return this.stackFrames.length > 1 ? this.stackFrames[this.stackFrames.length - 2] : null; -}; - -/** - * Push a reported value to the parent of the current stack frame. - * @param {*} value Reported value to push. - */ -Thread.prototype.pushReportedValue = function (value) { - const parentStackFrame = this.peekParentStackFrame(); - if (parentStackFrame) { - const waitingReporter = parentStackFrame.waitingReporter; - parentStackFrame.reported[waitingReporter] = value; + /** + * Thread status for initialized or running thread. + * This is the default state for a thread - execution should run normally, + * stepping from block to block. + * @const + */ + static get STATUS_RUNNING () { + return 0; } -}; -/** - * Add a parameter to the stack frame. - * Use when calling a procedure with parameter values. - * @param {!string} paramName Name of parameter. - * @param {*} value Value to set for parameter. - */ -Thread.prototype.pushParam = function (paramName, value) { - const stackFrame = this.peekStackFrame(); - stackFrame.params[paramName] = value; -}; + /** + * Threads are in this state when a primitive is waiting on a promise; + * execution is paused until the promise changes thread status. + * @const + */ + static get STATUS_PROMISE_WAIT () { + return 1; + } -/** - * Get a parameter at the lowest possible level of the stack. - * @param {!string} paramName Name of parameter. - * @return {*} value Value for parameter. - */ -Thread.prototype.getParam = function (paramName) { - for (let i = this.stackFrames.length - 1; i >= 0; i--) { - const frame = this.stackFrames[i]; - if (frame.params.hasOwnProperty(paramName)) { - return frame.params[paramName]; + /** + * Thread status for yield. + * @const + */ + static get STATUS_YIELD () { + return 2; + } + + /** + * Thread status for a single-tick yield. This will be cleared when the + * thread is resumed. + * @const + */ + static get STATUS_YIELD_TICK () { + return 3; + } + + /** + * Thread status for a finished/done thread. + * Thread is in this state when there are no more blocks to execute. + * @const + */ + static get STATUS_DONE () { + return 4; + } + + /** + * Push stack and update stack frames appropriately. + * @param {string} blockId Block ID to push to stack. + */ + pushStack (blockId) { + this.stack.push(blockId); + // Push an empty stack frame, if we need one. + // Might not, if we just popped the stack. + if (this.stack.length > this.stackFrames.length) { + // Copy warp mode from any higher level. + let warpMode = false; + if (this.stackFrames.length > 0 && this.stackFrames[this.stackFrames.length - 1]) { + warpMode = this.stackFrames[this.stackFrames.length - 1].warpMode; + } + this.stackFrames.push({ + isLoop: false, // Whether this level of the stack is a loop. + warpMode: warpMode, // Whether this level is in warp mode. + reported: {}, // Collects reported input values. + waitingReporter: null, // Name of waiting reporter. + params: {}, // Procedure parameters. + executionContext: {} // A context passed to block implementations. + }); } } - 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. - */ -Thread.prototype.atStackTop = function () { - return this.peekStack() === this.topBlock; -}; - - -/** - * Switch the thread to the next block at the current level of the stack. - * For example, this is used in a standard sequence of blocks, - * where execution proceeds from one block to the next. - */ -Thread.prototype.goToNextBlock = function () { - const nextBlockId = this.target.blocks.getNextBlock(this.peekStack()); - this.reuseStackForNextBlock(nextBlockId); -}; - -/** - * Attempt to determine whether a procedure call is recursive, - * by examining the stack. - * @param {!string} procedureCode Procedure code of procedure being called. - * @return {boolean} True if the call appears recursive. - */ -Thread.prototype.isRecursiveCall = function (procedureCode) { - let callCount = 5; // Max number of enclosing procedure calls to examine. - const sp = this.stack.length - 1; - for (let i = sp - 1; i >= 0; i--) { - const block = this.target.blocks.getBlock(this.stack[i]); - if (block.opcode === 'procedures_callnoreturn' && - block.mutation.proccode === procedureCode) { - return true; - } - if (--callCount < 0) return false; + /** + * Reset the stack frame for use by the next block. + * (avoids popping and re-pushing a new stack frame - keeps the warpmode the same + * @param {string} blockId Block ID to push to stack. + */ + reuseStackForNextBlock (blockId) { + this.stack[this.stack.length - 1] = blockId; + const frame = this.stackFrames[this.stackFrames.length - 1]; + frame.isLoop = false; + // frame.warpMode = warpMode; // warp mode stays the same when reusing the stack frame. + frame.reported = {}; + frame.waitingReporter = null; + frame.params = {}; + frame.executionContext = {}; } - return false; -}; + + /** + * Pop last block on the stack and its stack frame. + * @return {string} Block ID popped from the stack. + */ + popStack () { + this.stackFrames.pop(); + return this.stack.pop(); + } + + /** + * Pop back down the stack frame until we hit a procedure call or the stack frame is emptied + */ + stopThisScript () { + let blockID = this.peekStack(); + while (blockID !== null) { + const block = this.target.blocks.getBlock(blockID); + if (typeof block !== 'undefined' && block.opcode === 'procedures_callnoreturn') { + break; + } + this.popStack(); + blockID = this.peekStack(); + } + + if (this.stack.length === 0) { + // Clean up! + this.requestScriptGlowInFrame = false; + this.status = Thread.STATUS_DONE; + } + } + + /** + * Get top stack item. + * @return {?string} Block ID on top of stack. + */ + peekStack () { + return this.stack.length > 0 ? this.stack[this.stack.length - 1] : null; + } + + + /** + * Get top stack frame. + * @return {?object} Last stack frame stored on this thread. + */ + peekStackFrame () { + return this.stackFrames.length > 0 ? this.stackFrames[this.stackFrames.length - 1] : null; + } + + /** + * Get stack frame above the current top. + * @return {?object} Second to last stack frame stored on this thread. + */ + peekParentStackFrame () { + return this.stackFrames.length > 1 ? this.stackFrames[this.stackFrames.length - 2] : null; + } + + /** + * Push a reported value to the parent of the current stack frame. + * @param {*} value Reported value to push. + */ + pushReportedValue (value) { + const parentStackFrame = this.peekParentStackFrame(); + if (parentStackFrame) { + const waitingReporter = parentStackFrame.waitingReporter; + parentStackFrame.reported[waitingReporter] = value; + } + } + + /** + * Add a parameter to the stack frame. + * Use when calling a procedure with parameter values. + * @param {!string} paramName Name of parameter. + * @param {*} value Value to set for parameter. + */ + pushParam (paramName, value) { + const stackFrame = this.peekStackFrame(); + stackFrame.params[paramName] = value; + } + + /** + * Get a parameter at the lowest possible level of the stack. + * @param {!string} paramName Name of parameter. + * @return {*} value Value for parameter. + */ + getParam (paramName) { + for (let i = this.stackFrames.length - 1; i >= 0; i--) { + const 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. + */ + atStackTop () { + return this.peekStack() === this.topBlock; + } + + + /** + * Switch the thread to the next block at the current level of the stack. + * For example, this is used in a standard sequence of blocks, + * where execution proceeds from one block to the next. + */ + goToNextBlock () { + const nextBlockId = this.target.blocks.getNextBlock(this.peekStack()); + this.reuseStackForNextBlock(nextBlockId); + } + + /** + * Attempt to determine whether a procedure call is recursive, + * by examining the stack. + * @param {!string} procedureCode Procedure code of procedure being called. + * @return {boolean} True if the call appears recursive. + */ + isRecursiveCall (procedureCode) { + let callCount = 5; // Max number of enclosing procedure calls to examine. + const sp = this.stack.length - 1; + for (let i = sp - 1; i >= 0; i--) { + const block = this.target.blocks.getBlock(this.stack[i]); + if (block.opcode === 'procedures_callnoreturn' && + block.mutation.proccode === procedureCode) { + return true; + } + if (--callCount < 0) return false; + } + return false; + } +} module.exports = Thread; diff --git a/src/engine/variable.js b/src/engine/variable.js index eab090ca..2be2c0f8 100644 --- a/src/engine/variable.js +++ b/src/engine/variable.js @@ -3,16 +3,18 @@ * Object representing a Scratch variable. */ -/** - * @param {!string} name Name of the variable. - * @param {(string|number)} value Value of the variable. - * @param {boolean} isCloud Whether the variable is stored in the cloud. - * @constructor - */ -const Variable = function (name, value, isCloud) { - this.name = name; - this.value = value; - this.isCloud = isCloud; -}; +class Variable { + /** + * @param {!string} name Name of the variable. + * @param {(string|number)} value Value of the variable. + * @param {boolean} isCloud Whether the variable is stored in the cloud. + * @constructor + */ + constructor (name, value, isCloud) { + this.name = name; + this.value = value; + this.isCloud = isCloud; + } +} module.exports = Variable; diff --git a/src/io/clock.js b/src/io/clock.js index 6a28e30c..cbb9f242 100644 --- a/src/io/clock.js +++ b/src/io/clock.js @@ -1,37 +1,39 @@ const Timer = require('../util/timer'); -const Clock = function (runtime) { - this._projectTimer = new Timer(); - this._projectTimer.start(); - this._pausedTime = null; - this._paused = false; - /** - * Reference to the owning Runtime. - * @type{!Runtime} - */ - this.runtime = runtime; -}; - -Clock.prototype.projectTimer = function () { - if (this._paused) { - return this._pausedTime / 1000; +class Clock { + constructor (runtime) { + this._projectTimer = new Timer(); + this._projectTimer.start(); + this._pausedTime = null; + this._paused = false; + /** + * Reference to the owning Runtime. + * @type{!Runtime} + */ + this.runtime = runtime; } - return this._projectTimer.timeElapsed() / 1000; -}; -Clock.prototype.pause = function () { - this._paused = true; - this._pausedTime = this._projectTimer.timeElapsed(); -}; + projectTimer () { + if (this._paused) { + return this._pausedTime / 1000; + } + return this._projectTimer.timeElapsed() / 1000; + } -Clock.prototype.resume = function () { - this._paused = false; - const dt = this._projectTimer.timeElapsed() - this._pausedTime; - this._projectTimer.startTime += dt; -}; + pause () { + this._paused = true; + this._pausedTime = this._projectTimer.timeElapsed(); + } -Clock.prototype.resetProjectTimer = function () { - this._projectTimer.start(); -}; + resume () { + this._paused = false; + const dt = this._projectTimer.timeElapsed() - this._pausedTime; + this._projectTimer.startTime += dt; + } + + resetProjectTimer () { + this._projectTimer.start(); + } +} module.exports = Clock; diff --git a/src/io/keyboard.js b/src/io/keyboard.js index 7fe46dc3..4ff5ecc5 100644 --- a/src/io/keyboard.js +++ b/src/io/keyboard.js @@ -1,101 +1,103 @@ const Cast = require('../util/cast'); -const Keyboard = function (runtime) { +class Keyboard { + constructor (runtime) { + /** + * List of currently pressed keys. + * @type{Array.} + */ + this._keysPressed = []; + /** + * Reference to the owning Runtime. + * Can be used, for example, to activate hats. + * @type{!Runtime} + */ + this.runtime = runtime; + } + /** - * List of currently pressed keys. - * @type{Array.} + * Convert a Scratch key name to a DOM keyCode. + * @param {Any} keyName Scratch key argument. + * @return {number} Key code corresponding to a DOM event. + * @private */ - this._keysPressed = []; + _scratchKeyToKeyCode (keyName) { + if (typeof keyName === 'number') { + // Key codes placed in with number blocks. + return keyName; + } + const keyString = Cast.toString(keyName); + switch (keyString) { + case 'space': return 32; + case 'left arrow': return 37; + case 'up arrow': return 38; + case 'right arrow': return 39; + case 'down arrow': return 40; + // @todo: Consider adding other special keys here. + } + // Keys reported by DOM keyCode are upper case. + return keyString.toUpperCase().charCodeAt(0); + } + /** - * Reference to the owning Runtime. - * Can be used, for example, to activate hats. - * @type{!Runtime} + * Convert a DOM keyCode into a Scratch key name. + * @param {number} keyCode Key code from DOM event. + * @return {Any} Scratch key argument. + * @private */ - this.runtime = runtime; -}; + _keyCodeToScratchKey (keyCode) { + if (keyCode >= 48 && keyCode <= 90) { + // Standard letter. + return String.fromCharCode(keyCode).toLowerCase(); + } + switch (keyCode) { + case 32: return 'space'; + case 37: return 'left arrow'; + case 38: return 'up arrow'; + case 39: return 'right arrow'; + case 40: return 'down arrow'; + } + return ''; + } -/** - * Convert a Scratch key name to a DOM keyCode. - * @param {Any} keyName Scratch key argument. - * @return {number} Key code corresponding to a DOM event. - * @private - */ -Keyboard.prototype._scratchKeyToKeyCode = function (keyName) { - if (typeof keyName === 'number') { - // Key codes placed in with number blocks. - return keyName; - } - const keyString = Cast.toString(keyName); - switch (keyString) { - case 'space': return 32; - case 'left arrow': return 37; - case 'up arrow': return 38; - case 'right arrow': return 39; - case 'down arrow': return 40; - // @todo: Consider adding other special keys here. - } - // Keys reported by DOM keyCode are upper case. - return keyString.toUpperCase().charCodeAt(0); -}; - -/** - * Convert a DOM keyCode into a Scratch key name. - * @param {number} keyCode Key code from DOM event. - * @return {Any} Scratch key argument. - * @private - */ -Keyboard.prototype._keyCodeToScratchKey = function (keyCode) { - if (keyCode >= 48 && keyCode <= 90) { - // Standard letter. - return String.fromCharCode(keyCode).toLowerCase(); - } - switch (keyCode) { - case 32: return 'space'; - case 37: return 'left arrow'; - case 38: return 'up arrow'; - case 39: return 'right arrow'; - case 40: return 'down arrow'; - } - return ''; -}; - -/** - * Keyboard DOM event handler. - * @param {object} data Data from DOM event. - */ -Keyboard.prototype.postData = function (data) { - if (data.keyCode) { - const index = this._keysPressed.indexOf(data.keyCode); - if (data.isDown) { - // If not already present, add to the list. - if (index < 0) { - this._keysPressed.push(data.keyCode); + /** + * Keyboard DOM event handler. + * @param {object} data Data from DOM event. + */ + postData (data) { + if (data.keyCode) { + const index = this._keysPressed.indexOf(data.keyCode); + if (data.isDown) { + // If not already present, add to the list. + if (index < 0) { + this._keysPressed.push(data.keyCode); + } + // Always trigger hats, even if it was already pressed. + this.runtime.startHats('event_whenkeypressed', { + KEY_OPTION: this._keyCodeToScratchKey(data.keyCode) + }); + this.runtime.startHats('event_whenkeypressed', { + KEY_OPTION: 'any' + }); + } else if (index > -1) { + // If already present, remove from the list. + this._keysPressed.splice(index, 1); } - // Always trigger hats, even if it was already pressed. - this.runtime.startHats('event_whenkeypressed', { - KEY_OPTION: this._keyCodeToScratchKey(data.keyCode) - }); - this.runtime.startHats('event_whenkeypressed', { - KEY_OPTION: 'any' - }); - } else if (index > -1) { - // If already present, remove from the list. - this._keysPressed.splice(index, 1); } } -}; -/** - * Get key down state for a specified Scratch key name. - * @param {Any} key Scratch key argument. - * @return {boolean} Is the specified key down? - */ -Keyboard.prototype.getKeyIsDown = function (key) { - if (key === 'any') { - return this._keysPressed.length > 0; + /** + * Get key down state for a specified Scratch key name. + * @param {Any} key Scratch key argument. + * @return {boolean} Is the specified key down? + */ + getKeyIsDown (key) { + if (key === 'any') { + return this._keysPressed.length > 0; + } + const keyCode = this._scratchKeyToKeyCode(key); + return this._keysPressed.indexOf(keyCode) > -1; } - const keyCode = this._scratchKeyToKeyCode(key); - return this._keysPressed.indexOf(keyCode) > -1; -}; +} module.exports = Keyboard; diff --git a/src/io/mouse.js b/src/io/mouse.js index 9d2721a2..4c664717 100644 --- a/src/io/mouse.js +++ b/src/io/mouse.js @@ -1,79 +1,81 @@ const MathUtil = require('../util/math-util'); -const Mouse = function (runtime) { - this._x = 0; - this._y = 0; - this._isDown = false; - /** - * Reference to the owning Runtime. - * Can be used, for example, to activate hats. - * @type{!Runtime} - */ - this.runtime = runtime; -}; +class Mouse { + constructor (runtime) { + this._x = 0; + this._y = 0; + this._isDown = false; + /** + * Reference to the owning Runtime. + * Can be used, for example, to activate hats. + * @type{!Runtime} + */ + this.runtime = runtime; + } -/** - * Activate "event_whenthisspriteclicked" hats if needed. - * @param {number} x X position to be sent to the renderer. - * @param {number} y Y position to be sent to the renderer. - * @private - */ -Mouse.prototype._activateClickHats = function (x, y) { - if (this.runtime.renderer) { - const drawableID = this.runtime.renderer.pick(x, y); - for (let i = 0; i < this.runtime.targets.length; i++) { - const target = this.runtime.targets[i]; - if (target.hasOwnProperty('drawableID') && - target.drawableID === drawableID) { - this.runtime.startHats('event_whenthisspriteclicked', - null, target); - return; + /** + * Activate "event_whenthisspriteclicked" hats if needed. + * @param {number} x X position to be sent to the renderer. + * @param {number} y Y position to be sent to the renderer. + * @private + */ + _activateClickHats (x, y) { + if (this.runtime.renderer) { + const drawableID = this.runtime.renderer.pick(x, y); + for (let i = 0; i < this.runtime.targets.length; i++) { + const target = this.runtime.targets[i]; + if (target.hasOwnProperty('drawableID') && + target.drawableID === drawableID) { + this.runtime.startHats('event_whenthisspriteclicked', + null, target); + return; + } } } } -}; -/** - * Mouse DOM event handler. - * @param {object} data Data from DOM event. - */ -Mouse.prototype.postData = function (data) { - if (data.x) { - this._x = data.x - data.canvasWidth / 2; - } - if (data.y) { - this._y = data.y - data.canvasHeight / 2; - } - if (typeof data.isDown !== 'undefined') { - this._isDown = data.isDown; - if (!this._isDown) { - this._activateClickHats(data.x, data.y); + /** + * Mouse DOM event handler. + * @param {object} data Data from DOM event. + */ + postData (data) { + if (data.x) { + this._x = data.x - data.canvasWidth / 2; + } + if (data.y) { + this._y = data.y - data.canvasHeight / 2; + } + if (typeof data.isDown !== 'undefined') { + this._isDown = data.isDown; + if (!this._isDown) { + this._activateClickHats(data.x, data.y); + } } } -}; -/** - * Get the X position of the mouse. - * @return {number} Clamped X position of the mouse cursor. - */ -Mouse.prototype.getX = function () { - return MathUtil.clamp(this._x, -240, 240); -}; + /** + * Get the X position of the mouse. + * @return {number} Clamped X position of the mouse cursor. + */ + getX () { + return MathUtil.clamp(this._x, -240, 240); + } -/** - * Get the Y position of the mouse. - * @return {number} Clamped Y position of the mouse cursor. - */ -Mouse.prototype.getY = function () { - return MathUtil.clamp(-this._y, -180, 180); -}; + /** + * Get the Y position of the mouse. + * @return {number} Clamped Y position of the mouse cursor. + */ + getY () { + return MathUtil.clamp(-this._y, -180, 180); + } -/** - * Get the down state of the mouse. - * @return {boolean} Is the mouse down? - */ -Mouse.prototype.getIsDown = function () { - return this._isDown; -}; + /** + * Get the down state of the mouse. + * @return {boolean} Is the mouse down? + */ + getIsDown () { + return this._isDown; + } +} module.exports = Mouse; diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index f6465da4..e6323ec7 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -1,5 +1,3 @@ -const util = require('util'); - const log = require('../util/log'); const MathUtil = require('../util/math-util'); const Target = require('../engine/target'); @@ -10,789 +8,796 @@ const Target = require('../engine/target'); * @param {Runtime} runtime Reference to the runtime. * @constructor */ -const RenderedTarget = function (sprite, runtime) { - Target.call(this, sprite.blocks); - this.runtime = runtime; - /** - * Reference to the sprite that this is a render of. - * @type {!Sprite} - */ - this.sprite = sprite; - /** - * Reference to the global renderer for this VM, if one exists. - * @type {?RenderWebGLWorker} - */ - this.renderer = null; - if (this.runtime) { - this.renderer = this.runtime.renderer; - } - /** - * ID of the drawable for this rendered target, - * returned by the renderer, if rendered. - * @type {?Number} - */ - this.drawableID = null; - - /** - * Drag state of this rendered target. If true, x/y position can't be - * changed by blocks. - * @type {boolean} - */ - this.dragging = false; - - /** - * Map of current graphic effect values. - * @type {!Object.} - */ - this.effects = { - color: 0, - fisheye: 0, - whirl: 0, - pixelate: 0, - mosaic: 0, - brightness: 0, - ghost: 0 - }; -}; -util.inherits(RenderedTarget, Target); - -/** - * Create a drawable with the this.renderer. - */ -RenderedTarget.prototype.initDrawable = function () { - if (this.renderer) { - this.drawableID = this.renderer.createDrawable(); - } - // If we're a clone, start the hats. - if (!this.isOriginal) { - this.runtime.startHats( - 'control_start_as_clone', null, this - ); - } - - /** - * Audio player - */ - this.audioPlayer = null; - if (this.runtime && this.runtime.audioEngine) { - this.audioPlayer = this.runtime.audioEngine.createPlayer(); - } -}; - -/** - * Whether this represents an "original" non-clone rendered-target for a sprite, - * i.e., created by the editor and not clone blocks. - * @type {boolean} - */ -RenderedTarget.prototype.isOriginal = true; - -/** - * Whether this rendered target represents the Scratch stage. - * @type {boolean} - */ -RenderedTarget.prototype.isStage = false; - -/** - * Scratch X coordinate. Currently should range from -240 to 240. - * @type {Number} - */ -RenderedTarget.prototype.x = 0; - -/** - * Scratch Y coordinate. Currently should range from -180 to 180. - * @type {number} - */ -RenderedTarget.prototype.y = 0; - -/** - * Scratch direction. Currently should range from -179 to 180. - * @type {number} - */ -RenderedTarget.prototype.direction = 90; - -/** - * Whether the rendered target is draggable on the stage - * @type {boolean} - */ -RenderedTarget.prototype.draggable = false; - -/** - * Whether the rendered target is currently visible. - * @type {boolean} - */ -RenderedTarget.prototype.visible = true; - -/** - * Size of rendered target as a percent of costume size. - * @type {number} - */ -RenderedTarget.prototype.size = 100; - -/** - * Currently selected costume index. - * @type {number} - */ -RenderedTarget.prototype.currentCostume = 0; - -/** - * Event which fires when a target moves. - * @type {string} - */ -RenderedTarget.EVENT_TARGET_MOVED = 'TARGET_MOVED'; - -/** - * Rotation style for "all around"/spinning. - * @enum - */ -RenderedTarget.ROTATION_STYLE_ALL_AROUND = 'all around'; - -/** - * Rotation style for "left-right"/flipping. - * @enum - */ -RenderedTarget.ROTATION_STYLE_LEFT_RIGHT = 'left-right'; - -/** - * Rotation style for "no rotation." - * @enum - */ -RenderedTarget.ROTATION_STYLE_NONE = 'don\'t rotate'; - -/** - * Current rotation style. - * @type {!string} - */ -RenderedTarget.prototype.rotationStyle = ( - RenderedTarget.ROTATION_STYLE_ALL_AROUND -); - -/** - * Set the X and Y coordinates. - * @param {!number} x New X coordinate, in Scratch coordinates. - * @param {!number} y New Y coordinate, in Scratch coordinates. - * @param {?boolean} force Force setting X/Y, in case of dragging - */ -RenderedTarget.prototype.setXY = function (x, y, force) { - if (this.isStage) return; - if (this.dragging && !force) return; - const oldX = this.x; - const oldY = this.y; - if (this.renderer) { - const position = this.renderer.getFencedPositionOfDrawable(this.drawableID, [x, y]); - this.x = position[0]; - this.y = position[1]; - - this.renderer.updateDrawableProperties(this.drawableID, { - position: position - }); - if (this.visible) { - this.runtime.requestRedraw(); +class RenderedTarget extends Target { + constructor (sprite, runtime) { + super(sprite.blocks); + this.runtime = runtime; + /** + * Reference to the sprite that this is a render of. + * @type {!Sprite} + */ + this.sprite = sprite; + /** + * Reference to the global renderer for this VM, if one exists. + * @type {?RenderWebGLWorker} + */ + this.renderer = null; + if (this.runtime) { + this.renderer = this.runtime.renderer; } - } else { - this.x = x; - this.y = y; - } - this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY); - this.runtime.spriteInfoReport(this); -}; + /** + * ID of the drawable for this rendered target, + * returned by the renderer, if rendered. + * @type {?Number} + */ + this.drawableID = null; -/** - * Get the rendered direction and scale, after applying rotation style. - * @return {object} Direction and scale to render. - */ -RenderedTarget.prototype._getRenderedDirectionAndScale = function () { - // Default: no changes to `this.direction` or `this.scale`. - let finalDirection = this.direction; - let finalScale = [this.size, this.size]; - if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) { - // Force rendered direction to be 90. - finalDirection = 90; - } else if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) { - // Force rendered direction to be 90, and flip drawable if needed. - finalDirection = 90; - const scaleFlip = (this.direction < 0) ? -1 : 1; - finalScale = [scaleFlip * this.size, this.size]; - } - return {direction: finalDirection, scale: finalScale}; -}; + /** + * Drag state of this rendered target. If true, x/y position can't be + * changed by blocks. + * @type {boolean} + */ + this.dragging = false; -/** - * Set the direction. - * @param {!number} direction New direction. - */ -RenderedTarget.prototype.setDirection = function (direction) { - if (this.isStage) { - return; - } - // Keep direction between -179 and +180. - this.direction = MathUtil.wrapClamp(direction, -179, 180); - if (this.renderer) { - const renderedDirectionScale = this._getRenderedDirectionAndScale(); - this.renderer.updateDrawableProperties(this.drawableID, { - direction: renderedDirectionScale.direction, - scale: renderedDirectionScale.scale - }); - if (this.visible) { - this.runtime.requestRedraw(); - } - } - this.runtime.spriteInfoReport(this); -}; - -/** - * Set draggability; i.e., whether it's able to be dragged in the player - * @param {!boolean} draggable True if should be draggable. - */ -RenderedTarget.prototype.setDraggable = function (draggable) { - if (this.isStage) return; - this.draggable = !!draggable; - this.runtime.spriteInfoReport(this); -}; - -/** - * Set a say bubble. - * @param {?string} type Type of say bubble: "say", "think", or null. - * @param {?string} message Message to put in say bubble. - */ -RenderedTarget.prototype.setSay = function (type, message) { - if (this.isStage) { - return; - } - // @todo: Render to stage. - if (!type || !message) { - log.info('Clearing say bubble'); - return; - } - log.info('Setting say bubble:', type, message); -}; - -/** - * Set visibility; i.e., whether it's shown or hidden. - * @param {!boolean} visible True if should be shown. - */ -RenderedTarget.prototype.setVisible = function (visible) { - if (this.isStage) { - return; - } - this.visible = !!visible; - if (this.renderer) { - this.renderer.updateDrawableProperties(this.drawableID, { - visible: this.visible - }); - if (this.visible) { - this.runtime.requestRedraw(); - } - } - this.runtime.spriteInfoReport(this); -}; - -/** - * Set size, as a percentage of the costume size. - * @param {!number} size Size of rendered target, as % of costume size. - */ -RenderedTarget.prototype.setSize = function (size) { - if (this.isStage) { - return; - } - if (this.renderer) { - // Clamp to scales relative to costume and stage size. - // See original ScratchSprite.as:setSize. - const costumeSize = this.renderer.getSkinSize(this.drawableID); - const origW = costumeSize[0]; - const origH = costumeSize[1]; - const minScale = Math.min(1, Math.max(5 / origW, 5 / origH)); - const maxScale = Math.min( - (1.5 * this.runtime.constructor.STAGE_WIDTH) / origW, - (1.5 * this.runtime.constructor.STAGE_HEIGHT) / origH - ); - this.size = MathUtil.clamp(size / 100, minScale, maxScale) * 100; - const renderedDirectionScale = this._getRenderedDirectionAndScale(); - this.renderer.updateDrawableProperties(this.drawableID, { - direction: renderedDirectionScale.direction, - scale: renderedDirectionScale.scale - }); - if (this.visible) { - this.runtime.requestRedraw(); - } - } -}; - -/** - * Set a particular graphic effect value. - * @param {!string} effectName Name of effect (see `RenderedTarget.prototype.effects`). - * @param {!number} value Numerical magnitude of effect. - */ -RenderedTarget.prototype.setEffect = function (effectName, value) { - if (!this.effects.hasOwnProperty(effectName)) return; - this.effects[effectName] = value; - if (this.renderer) { - const props = {}; - props[effectName] = this.effects[effectName]; - this.renderer.updateDrawableProperties(this.drawableID, props); - if (this.visible) { - this.runtime.requestRedraw(); - } - } -}; - -/** - * Clear all graphic effects on this rendered target. - */ -RenderedTarget.prototype.clearEffects = function () { - for (const effectName in this.effects) { - if (!this.effects.hasOwnProperty(effectName)) continue; - this.effects[effectName] = 0; - } - if (this.renderer) { - this.renderer.updateDrawableProperties(this.drawableID, this.effects); - if (this.visible) { - this.runtime.requestRedraw(); - } - } -}; - -/** - * Set the current costume. - * @param {number} index New index of costume. - */ -RenderedTarget.prototype.setCostume = function (index) { - // Keep the costume index within possible values. - index = Math.round(index); - this.currentCostume = MathUtil.wrapClamp( - index, 0, this.sprite.costumes.length - 1 - ); - if (this.renderer) { - const costume = this.sprite.costumes[this.currentCostume]; - const drawableProperties = { - skinId: costume.skinId, - costumeResolution: costume.bitmapResolution + /** + * Map of current graphic effect values. + * @type {!Object.} + */ + this.effects = { + color: 0, + fisheye: 0, + whirl: 0, + pixelate: 0, + mosaic: 0, + brightness: 0, + ghost: 0 }; - if ( - typeof costume.rotationCenterX !== 'undefined' && - typeof costume.rotationCenterY !== 'undefined' - ) { - const scale = costume.bitmapResolution || 1; - drawableProperties.rotationCenter = [ - costume.rotationCenterX / scale, - costume.rotationCenterY / scale - ]; - } - this.renderer.updateDrawableProperties(this.drawableID, drawableProperties); - if (this.visible) { - this.runtime.requestRedraw(); - } - } - this.runtime.spriteInfoReport(this); -}; -/** - * Update the rotation style. - * @param {!string} rotationStyle New rotation style. - */ -RenderedTarget.prototype.setRotationStyle = function (rotationStyle) { - if (rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) { - this.rotationStyle = RenderedTarget.ROTATION_STYLE_NONE; - } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_ALL_AROUND) { + /** + * Whether this represents an "original" non-clone rendered-target for a sprite, + * i.e., created by the editor and not clone blocks. + * @type {boolean} + */ + this.isOriginal = true; + + /** + * Whether this rendered target represents the Scratch stage. + * @type {boolean} + */ + this.isStage = false; + + /** + * Scratch X coordinate. Currently should range from -240 to 240. + * @type {Number} + */ + this.x = 0; + + /** + * Scratch Y coordinate. Currently should range from -180 to 180. + * @type {number} + */ + this.y = 0; + + /** + * Scratch direction. Currently should range from -179 to 180. + * @type {number} + */ + this.direction = 90; + + /** + * Whether the rendered target is draggable on the stage + * @type {boolean} + */ + this.draggable = false; + + /** + * Whether the rendered target is currently visible. + * @type {boolean} + */ + this.visible = true; + + /** + * Size of rendered target as a percent of costume size. + * @type {number} + */ + this.size = 100; + + /** + * Currently selected costume index. + * @type {number} + */ + this.currentCostume = 0; + + /** + * Current rotation style. + * @type {!string} + */ this.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND; - } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) { - this.rotationStyle = RenderedTarget.ROTATION_STYLE_LEFT_RIGHT; } - if (this.renderer) { - const renderedDirectionScale = this._getRenderedDirectionAndScale(); - this.renderer.updateDrawableProperties(this.drawableID, { - direction: renderedDirectionScale.direction, - scale: renderedDirectionScale.scale - }); - if (this.visible) { - this.runtime.requestRedraw(); + + /** + * Create a drawable with the this.renderer. + */ + initDrawable () { + if (this.renderer) { + this.drawableID = this.renderer.createDrawable(); + } + // If we're a clone, start the hats. + if (!this.isOriginal) { + this.runtime.startHats( + 'control_start_as_clone', null, this + ); + } + + /** + * Audio player + */ + this.audioPlayer = null; + if (this.runtime && this.runtime.audioEngine) { + this.audioPlayer = this.runtime.audioEngine.createPlayer(); } } - this.runtime.spriteInfoReport(this); -}; -/** - * Get a costume index of this rendered target, by name of the costume. - * @param {?string} costumeName Name of a costume. - * @return {number} Index of the named costume, or -1 if not present. - */ -RenderedTarget.prototype.getCostumeIndexByName = function (costumeName) { - for (let i = 0; i < this.sprite.costumes.length; i++) { - if (this.sprite.costumes[i].name === costumeName) { - return i; + /** + * Event which fires when a target moves. + * @type {string} + */ + static get EVENT_TARGET_MOVED () { + return 'TARGET_MOVED'; + } + + /** + * Rotation style for "all around"/spinning. + * @type {string} + */ + static get ROTATION_STYLE_ALL_AROUND () { + return 'all around'; + } + + /** + * Rotation style for "left-right"/flipping. + * @type {string} + */ + static get ROTATION_STYLE_LEFT_RIGHT () { + return 'left-right'; + } + + /** + * Rotation style for "no rotation." + * @type {string} + */ + static get ROTATION_STYLE_NONE () { + return "don't rotate"; + } + + /** + * Set the X and Y coordinates. + * @param {!number} x New X coordinate, in Scratch coordinates. + * @param {!number} y New Y coordinate, in Scratch coordinates. + * @param {?boolean} force Force setting X/Y, in case of dragging + */ + setXY (x, y, force) { + if (this.isStage) return; + if (this.dragging && !force) return; + const oldX = this.x; + const oldY = this.y; + if (this.renderer) { + const position = this.renderer.getFencedPositionOfDrawable(this.drawableID, [x, y]); + this.x = position[0]; + this.y = position[1]; + + this.renderer.updateDrawableProperties(this.drawableID, { + position: position + }); + if (this.visible) { + this.runtime.requestRedraw(); + } + } else { + this.x = x; + this.y = y; + } + this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY); + this.runtime.spriteInfoReport(this); + } + + /** + * Get the rendered direction and scale, after applying rotation style. + * @return {object} Direction and scale to render. + */ + _getRenderedDirectionAndScale () { + // Default: no changes to `this.direction` or `this.scale`. + let finalDirection = this.direction; + let finalScale = [this.size, this.size]; + if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) { + // Force rendered direction to be 90. + finalDirection = 90; + } else if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) { + // Force rendered direction to be 90, and flip drawable if needed. + finalDirection = 90; + const scaleFlip = (this.direction < 0) ? -1 : 1; + finalScale = [scaleFlip * this.size, this.size]; + } + return {direction: finalDirection, scale: finalScale}; + } + + /** + * Set the direction. + * @param {!number} direction New direction. + */ + setDirection (direction) { + if (this.isStage) { + return; + } + // Keep direction between -179 and +180. + this.direction = MathUtil.wrapClamp(direction, -179, 180); + if (this.renderer) { + const renderedDirectionScale = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawableProperties(this.drawableID, { + direction: renderedDirectionScale.direction, + scale: renderedDirectionScale.scale + }); + if (this.visible) { + this.runtime.requestRedraw(); + } + } + this.runtime.spriteInfoReport(this); + } + + /** + * Set draggability; i.e., whether it's able to be dragged in the player + * @param {!boolean} draggable True if should be draggable. + */ + setDraggable (draggable) { + if (this.isStage) return; + this.draggable = !!draggable; + this.runtime.spriteInfoReport(this); + } + + /** + * Set a say bubble. + * @param {?string} type Type of say bubble: "say", "think", or null. + * @param {?string} message Message to put in say bubble. + */ + setSay (type, message) { + if (this.isStage) { + return; + } + // @todo: Render to stage. + if (!type || !message) { + log.info('Clearing say bubble'); + return; + } + log.info('Setting say bubble:', type, message); + } + + /** + * Set visibility; i.e., whether it's shown or hidden. + * @param {!boolean} visible True if should be shown. + */ + setVisible (visible) { + if (this.isStage) { + return; + } + this.visible = !!visible; + if (this.renderer) { + this.renderer.updateDrawableProperties(this.drawableID, { + visible: this.visible + }); + if (this.visible) { + this.runtime.requestRedraw(); + } + } + this.runtime.spriteInfoReport(this); + } + + /** + * Set size, as a percentage of the costume size. + * @param {!number} size Size of rendered target, as % of costume size. + */ + setSize (size) { + if (this.isStage) { + return; + } + if (this.renderer) { + // Clamp to scales relative to costume and stage size. + // See original ScratchSprite.as:setSize. + const costumeSize = this.renderer.getSkinSize(this.drawableID); + const origW = costumeSize[0]; + const origH = costumeSize[1]; + const minScale = Math.min(1, Math.max(5 / origW, 5 / origH)); + const maxScale = Math.min( + (1.5 * this.runtime.constructor.STAGE_WIDTH) / origW, + (1.5 * this.runtime.constructor.STAGE_HEIGHT) / origH + ); + this.size = MathUtil.clamp(size / 100, minScale, maxScale) * 100; + const renderedDirectionScale = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawableProperties(this.drawableID, { + direction: renderedDirectionScale.direction, + scale: renderedDirectionScale.scale + }); + if (this.visible) { + this.runtime.requestRedraw(); + } } } - return -1; -}; -/** - * Get a costume of this rendered target by id. - * @return {object} current costume - */ -RenderedTarget.prototype.getCurrentCostume = function () { - return this.sprite.costumes[this.currentCostume]; -}; + /** + * Set a particular graphic effect value. + * @param {!string} effectName Name of effect (see `RenderedTarget.prototype.effects`). + * @param {!number} value Numerical magnitude of effect. + */ + setEffect (effectName, value) { + if (!this.effects.hasOwnProperty(effectName)) return; + this.effects[effectName] = value; + if (this.renderer) { + const props = {}; + props[effectName] = this.effects[effectName]; + this.renderer.updateDrawableProperties(this.drawableID, props); + if (this.visible) { + this.runtime.requestRedraw(); + } + } + } -/** - * Get full costume list - * @return {object[]} list of costumes - */ -RenderedTarget.prototype.getCostumes = function () { - return this.sprite.costumes; -}; - -/** - * Get full sound list - * @return {object[]} list of sounds - */ -RenderedTarget.prototype.getSounds = function () { - return this.sprite.sounds; -}; - -/** - * Update all drawable properties for this rendered target. - * Use when a batch has changed, e.g., when the drawable is first created. - */ -RenderedTarget.prototype.updateAllDrawableProperties = function () { - if (this.renderer) { - const renderedDirectionScale = this._getRenderedDirectionAndScale(); - const costume = this.sprite.costumes[this.currentCostume]; - const bitmapResolution = costume.bitmapResolution || 1; - const props = { - position: [this.x, this.y], - direction: renderedDirectionScale.direction, - draggable: this.draggable, - scale: renderedDirectionScale.scale, - visible: this.visible, - skinId: costume.skinId, - costumeResolution: bitmapResolution, - rotationCenter: [ - costume.rotationCenterX / bitmapResolution, - costume.rotationCenterY / bitmapResolution - ] - }; + /** + * Clear all graphic effects on this rendered target. + */ + clearEffects () { for (const effectName in this.effects) { if (!this.effects.hasOwnProperty(effectName)) continue; - props[effectName] = this.effects[effectName]; + this.effects[effectName] = 0; } - this.renderer.updateDrawableProperties(this.drawableID, props); - if (this.visible) { - this.runtime.requestRedraw(); + if (this.renderer) { + this.renderer.updateDrawableProperties(this.drawableID, this.effects); + if (this.visible) { + this.runtime.requestRedraw(); + } } } - this.runtime.spriteInfoReport(this); -}; -/** - * Return the human-readable name for this rendered target, e.g., the sprite's name. - * @override - * @returns {string} Human-readable name. - */ -RenderedTarget.prototype.getName = function () { - return this.sprite.name; -}; - -/** - * Return whether this rendered target is a sprite (not a clone, not the stage). - * @return {boolean} True if not a clone and not the stage. - */ -RenderedTarget.prototype.isSprite = function () { - return !this.isStage && this.isOriginal; -}; - -/** - * Return the rendered target's tight bounding box. - * Includes top, left, bottom, right attributes in Scratch coordinates. - * @return {?object} Tight bounding box, or null. - */ -RenderedTarget.prototype.getBounds = function () { - if (this.renderer) { - return this.runtime.renderer.getBounds(this.drawableID); - } - return null; -}; - -/** - * Return whether touching a point. - * @param {number} x X coordinate of test point. - * @param {number} y Y coordinate of test point. - * @return {boolean} True iff the rendered target is touching the point. - */ -RenderedTarget.prototype.isTouchingPoint = function (x, y) { - if (this.renderer) { - // @todo: Update once pick is in Scratch coordinates. - // Limits test to this Drawable, so this will return true - // even if the clone is obscured by another Drawable. - const pickResult = this.runtime.renderer.pick( - x + (this.runtime.constructor.STAGE_WIDTH / 2), - -y + (this.runtime.constructor.STAGE_HEIGHT / 2), - null, null, - [this.drawableID] + /** + * Set the current costume. + * @param {number} index New index of costume. + */ + setCostume (index) { + // Keep the costume index within possible values. + index = Math.round(index); + this.currentCostume = MathUtil.wrapClamp( + index, 0, this.sprite.costumes.length - 1 ); - return pickResult === this.drawableID; - } - return false; -}; - -/** - * Return whether touching a stage edge. - * @return {boolean} True iff the rendered target is touching the stage edge. - */ -RenderedTarget.prototype.isTouchingEdge = function () { - if (this.renderer) { - const stageWidth = this.runtime.constructor.STAGE_WIDTH; - const stageHeight = this.runtime.constructor.STAGE_HEIGHT; - const bounds = this.getBounds(); - if (bounds.left < -stageWidth / 2 || - bounds.right > stageWidth / 2 || - bounds.top > stageHeight / 2 || - bounds.bottom < -stageHeight / 2) { - return true; + if (this.renderer) { + const costume = this.sprite.costumes[this.currentCostume]; + const drawableProperties = { + skinId: costume.skinId, + costumeResolution: costume.bitmapResolution + }; + if ( + typeof costume.rotationCenterX !== 'undefined' && + typeof costume.rotationCenterY !== 'undefined' + ) { + const scale = costume.bitmapResolution || 1; + drawableProperties.rotationCenter = [ + costume.rotationCenterX / scale, + costume.rotationCenterY / scale + ]; + } + this.renderer.updateDrawableProperties(this.drawableID, drawableProperties); + if (this.visible) { + this.runtime.requestRedraw(); + } } + this.runtime.spriteInfoReport(this); } - return false; -}; -/** - * Return whether touching any of a named sprite's clones. - * @param {string} spriteName Name of the sprite. - * @return {boolean} True iff touching a clone of the sprite. - */ -RenderedTarget.prototype.isTouchingSprite = function (spriteName) { - const firstClone = this.runtime.getSpriteTargetByName(spriteName); - if (!firstClone || !this.renderer) { + /** + * Update the rotation style. + * @param {!string} rotationStyle New rotation style. + */ + setRotationStyle (rotationStyle) { + if (rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) { + this.rotationStyle = RenderedTarget.ROTATION_STYLE_NONE; + } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_ALL_AROUND) { + this.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND; + } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) { + this.rotationStyle = RenderedTarget.ROTATION_STYLE_LEFT_RIGHT; + } + if (this.renderer) { + const renderedDirectionScale = this._getRenderedDirectionAndScale(); + this.renderer.updateDrawableProperties(this.drawableID, { + direction: renderedDirectionScale.direction, + scale: renderedDirectionScale.scale + }); + if (this.visible) { + this.runtime.requestRedraw(); + } + } + this.runtime.spriteInfoReport(this); + } + + /** + * Get a costume index of this rendered target, by name of the costume. + * @param {?string} costumeName Name of a costume. + * @return {number} Index of the named costume, or -1 if not present. + */ + getCostumeIndexByName (costumeName) { + for (let i = 0; i < this.sprite.costumes.length; i++) { + if (this.sprite.costumes[i].name === costumeName) { + return i; + } + } + return -1; + } + + /** + * Get a costume of this rendered target by id. + * @return {object} current costume + */ + getCurrentCostume () { + return this.sprite.costumes[this.currentCostume]; + } + + /** + * Get full costume list + * @return {object[]} list of costumes + */ + getCostumes () { + return this.sprite.costumes; + } + + /** + * Get full sound list + * @return {object[]} list of sounds + */ + getSounds () { + return this.sprite.sounds; + } + + /** + * Update all drawable properties for this rendered target. + * Use when a batch has changed, e.g., when the drawable is first created. + */ + updateAllDrawableProperties () { + if (this.renderer) { + const renderedDirectionScale = this._getRenderedDirectionAndScale(); + const costume = this.sprite.costumes[this.currentCostume]; + const bitmapResolution = costume.bitmapResolution || 1; + const props = { + position: [this.x, this.y], + direction: renderedDirectionScale.direction, + draggable: this.draggable, + scale: renderedDirectionScale.scale, + visible: this.visible, + skinId: costume.skinId, + costumeResolution: bitmapResolution, + rotationCenter: [ + costume.rotationCenterX / bitmapResolution, + costume.rotationCenterY / bitmapResolution + ] + }; + for (const effectName in this.effects) { + if (!this.effects.hasOwnProperty(effectName)) continue; + props[effectName] = this.effects[effectName]; + } + this.renderer.updateDrawableProperties(this.drawableID, props); + if (this.visible) { + this.runtime.requestRedraw(); + } + } + this.runtime.spriteInfoReport(this); + } + + /** + * Return the human-readable name for this rendered target, e.g., the sprite's name. + * @override + * @returns {string} Human-readable name. + */ + getName () { + return this.sprite.name; + } + + /** + * Return whether this rendered target is a sprite (not a clone, not the stage). + * @return {boolean} True if not a clone and not the stage. + */ + isSprite () { + return !this.isStage && this.isOriginal; + } + + /** + * Return the rendered target's tight bounding box. + * Includes top, left, bottom, right attributes in Scratch coordinates. + * @return {?object} Tight bounding box, or null. + */ + getBounds () { + if (this.renderer) { + return this.runtime.renderer.getBounds(this.drawableID); + } + return null; + } + + /** + * Return whether touching a point. + * @param {number} x X coordinate of test point. + * @param {number} y Y coordinate of test point. + * @return {boolean} True iff the rendered target is touching the point. + */ + isTouchingPoint (x, y) { + if (this.renderer) { + // @todo: Update once pick is in Scratch coordinates. + // Limits test to this Drawable, so this will return true + // even if the clone is obscured by another Drawable. + const pickResult = this.runtime.renderer.pick( + x + (this.runtime.constructor.STAGE_WIDTH / 2), + -y + (this.runtime.constructor.STAGE_HEIGHT / 2), + null, null, + [this.drawableID] + ); + return pickResult === this.drawableID; + } return false; } - const drawableCandidates = firstClone.sprite.clones.map(clone => clone.drawableID); - return this.renderer.isTouchingDrawables( - this.drawableID, drawableCandidates); -}; -/** - * Return whether touching a color. - * @param {Array.} rgb [r,g,b], values between 0-255. - * @return {Promise.} True iff the rendered target is touching the color. - */ -RenderedTarget.prototype.isTouchingColor = function (rgb) { - if (this.renderer) { - return this.renderer.isTouchingColor(this.drawableID, rgb); + /** + * Return whether touching a stage edge. + * @return {boolean} True iff the rendered target is touching the stage edge. + */ + isTouchingEdge () { + if (this.renderer) { + const stageWidth = this.runtime.constructor.STAGE_WIDTH; + const stageHeight = this.runtime.constructor.STAGE_HEIGHT; + const bounds = this.getBounds(); + if (bounds.left < -stageWidth / 2 || + bounds.right > stageWidth / 2 || + bounds.top > stageHeight / 2 || + bounds.bottom < -stageHeight / 2) { + return true; + } + } + return false; } - return false; -}; -/** - * Return whether rendered target's color is touching a color. - * @param {object} targetRgb {Array.} [r,g,b], values between 0-255. - * @param {object} maskRgb {Array.} [r,g,b], values between 0-255. - * @return {Promise.} True iff the color is touching the color. - */ -RenderedTarget.prototype.colorIsTouchingColor = function (targetRgb, maskRgb) { - if (this.renderer) { - return this.renderer.isTouchingColor( - this.drawableID, - targetRgb, - maskRgb - ); + /** + * Return whether touching any of a named sprite's clones. + * @param {string} spriteName Name of the sprite. + * @return {boolean} True iff touching a clone of the sprite. + */ + isTouchingSprite (spriteName) { + const firstClone = this.runtime.getSpriteTargetByName(spriteName); + if (!firstClone || !this.renderer) { + return false; + } + const drawableCandidates = firstClone.sprite.clones.map(clone => clone.drawableID); + return this.renderer.isTouchingDrawables( + this.drawableID, drawableCandidates); } - return false; -}; -/** - * Move to the front layer. - */ -RenderedTarget.prototype.goToFront = function () { - if (this.renderer) { - this.renderer.setDrawableOrder(this.drawableID, Infinity); + /** + * Return whether touching a color. + * @param {Array.} rgb [r,g,b], values between 0-255. + * @return {Promise.} True iff the rendered target is touching the color. + */ + isTouchingColor (rgb) { + if (this.renderer) { + return this.renderer.isTouchingColor(this.drawableID, rgb); + } + return false; } -}; -/** - * Move back a number of layers. - * @param {number} nLayers How many layers to go back. - */ -RenderedTarget.prototype.goBackLayers = function (nLayers) { - if (this.renderer) { - this.renderer.setDrawableOrder(this.drawableID, -nLayers, true, 1); + /** + * Return whether rendered target's color is touching a color. + * @param {object} targetRgb {Array.} [r,g,b], values between 0-255. + * @param {object} maskRgb {Array.} [r,g,b], values between 0-255. + * @return {Promise.} True iff the color is touching the color. + */ + colorIsTouchingColor (targetRgb, maskRgb) { + if (this.renderer) { + return this.renderer.isTouchingColor( + this.drawableID, + targetRgb, + maskRgb + ); + } + return false; } -}; -/** - * Move behind some other rendered target. - * @param {!RenderedTarget} other Other rendered target to move behind. - */ -RenderedTarget.prototype.goBehindOther = function (other) { - if (this.renderer) { - const otherLayer = this.renderer.setDrawableOrder( - other.drawableID, 0, true); - this.renderer.setDrawableOrder(this.drawableID, otherLayer); - } -}; - -/** - * Keep a desired position within a fence. - * @param {number} newX New desired X position. - * @param {number} newY New desired Y position. - * @param {object=} optFence Optional fence with left, right, top bottom. - * @return {Array.} Fenced X and Y coordinates. - */ -RenderedTarget.prototype.keepInFence = function (newX, newY, optFence) { - let fence = optFence; - if (!fence) { - fence = { - left: -this.runtime.constructor.STAGE_WIDTH / 2, - right: this.runtime.constructor.STAGE_WIDTH / 2, - top: this.runtime.constructor.STAGE_HEIGHT / 2, - bottom: -this.runtime.constructor.STAGE_HEIGHT / 2 - }; - } - const bounds = this.getBounds(); - if (!bounds) return; - // Adjust the known bounds to the target position. - bounds.left += (newX - this.x); - bounds.right += (newX - this.x); - bounds.top += (newY - this.y); - bounds.bottom += (newY - this.y); - // Find how far we need to move the target position. - let dx = 0; - let dy = 0; - if (bounds.left < fence.left) { - dx += fence.left - bounds.left; - } - if (bounds.right > fence.right) { - dx += fence.right - bounds.right; - } - if (bounds.top > fence.top) { - dy += fence.top - bounds.top; - } - if (bounds.bottom < fence.bottom) { - dy += fence.bottom - bounds.bottom; - } - return [newX + dx, newY + dy]; -}; - -/** - * Make a clone, copying any run-time properties. - * If we've hit the global clone limit, returns null. - * @return {RenderedTarget} New clone. - */ -RenderedTarget.prototype.makeClone = function () { - if (!this.runtime.clonesAvailable() || this.isStage) { - return null; // Hit max clone limit, or this is the stage. - } - this.runtime.changeCloneCounter(1); - const newClone = this.sprite.createClone(); - // Copy all properties. - newClone.x = this.x; - newClone.y = this.y; - newClone.direction = this.direction; - newClone.draggable = this.draggable; - newClone.visible = this.visible; - newClone.size = this.size; - newClone.currentCostume = this.currentCostume; - newClone.rotationStyle = this.rotationStyle; - newClone.effects = JSON.parse(JSON.stringify(this.effects)); - newClone.variables = JSON.parse(JSON.stringify(this.variables)); - newClone.lists = JSON.parse(JSON.stringify(this.lists)); - newClone.initDrawable(); - newClone.updateAllDrawableProperties(); - // Place behind the current target. - newClone.goBehindOther(this); - return newClone; -}; - -/** - * Called when the project receives a "green flag." - * For a rendered target, this clears graphic effects. - */ -RenderedTarget.prototype.onGreenFlag = function () { - this.clearEffects(); -}; - -/** - * Called when the project receives a "stop all" - * Stop all sounds and clear graphic effects. - */ -RenderedTarget.prototype.onStopAll = function () { - this.clearEffects(); - if (this.audioPlayer) { - this.audioPlayer.stopAllSounds(); - this.audioPlayer.clearEffects(); - } -}; - -/** - * Post/edit sprite info. - * @param {object} data An object with sprite info data to set. - */ -RenderedTarget.prototype.postSpriteInfo = function (data) { - const force = data.hasOwnProperty('force') ? data.force : null; - if (data.hasOwnProperty('x')) { - this.setXY(data.x, this.y, force); - } - if (data.hasOwnProperty('y')) { - this.setXY(this.x, data.y, force); - } - if (data.hasOwnProperty('direction')) { - this.setDirection(data.direction); - } - if (data.hasOwnProperty('draggable')) { - this.setDraggable(data.draggable); - } - if (data.hasOwnProperty('rotationStyle')) { - this.setRotationStyle(data.rotationStyle); - } - if (data.hasOwnProperty('visible')) { - this.setVisible(data.visible); - } -}; - -/** - * Put the sprite into the drag state. While in effect, setXY must be forced - */ -RenderedTarget.prototype.startDrag = function () { - this.dragging = true; -}; - -/** - * Remove the sprite from the drag state. - */ -RenderedTarget.prototype.stopDrag = function () { - this.dragging = false; -}; - -/** - * Serialize sprite info, used when emitting events about the sprite - * @returns {object} sprite data as a simple object - */ -RenderedTarget.prototype.toJSON = function () { - return { - id: this.id, - name: this.getName(), - isStage: this.isStage, - x: this.x, - y: this.y, - size: this.size, - direction: this.direction, - draggable: this.draggable, - costume: this.getCurrentCostume(), - costumes: this.getCostumes(), - sounds: this.getSounds(), - costumeCount: this.getCostumes().length, - visible: this.visible, - rotationStyle: this.rotationStyle - }; -}; - -/** - * Dispose, destroying any run-time properties. - */ -RenderedTarget.prototype.dispose = function () { - this.runtime.changeCloneCounter(-1); - if (this.renderer && this.drawableID !== null) { - this.renderer.destroyDrawable(this.drawableID); - if (this.visible) { - this.runtime.requestRedraw(); + /** + * Move to the front layer. + */ + goToFront () { + if (this.renderer) { + this.renderer.setDrawableOrder(this.drawableID, Infinity); } } -}; + + /** + * Move back a number of layers. + * @param {number} nLayers How many layers to go back. + */ + goBackLayers (nLayers) { + if (this.renderer) { + this.renderer.setDrawableOrder(this.drawableID, -nLayers, true, 1); + } + } + + /** + * Move behind some other rendered target. + * @param {!RenderedTarget} other Other rendered target to move behind. + */ + goBehindOther (other) { + if (this.renderer) { + const otherLayer = this.renderer.setDrawableOrder( + other.drawableID, 0, true); + this.renderer.setDrawableOrder(this.drawableID, otherLayer); + } + } + + /** + * Keep a desired position within a fence. + * @param {number} newX New desired X position. + * @param {number} newY New desired Y position. + * @param {object=} optFence Optional fence with left, right, top bottom. + * @return {Array.} Fenced X and Y coordinates. + */ + keepInFence (newX, newY, optFence) { + let fence = optFence; + if (!fence) { + fence = { + left: -this.runtime.constructor.STAGE_WIDTH / 2, + right: this.runtime.constructor.STAGE_WIDTH / 2, + top: this.runtime.constructor.STAGE_HEIGHT / 2, + bottom: -this.runtime.constructor.STAGE_HEIGHT / 2 + }; + } + const bounds = this.getBounds(); + if (!bounds) return; + // Adjust the known bounds to the target position. + bounds.left += (newX - this.x); + bounds.right += (newX - this.x); + bounds.top += (newY - this.y); + bounds.bottom += (newY - this.y); + // Find how far we need to move the target position. + let dx = 0; + let dy = 0; + if (bounds.left < fence.left) { + dx += fence.left - bounds.left; + } + if (bounds.right > fence.right) { + dx += fence.right - bounds.right; + } + if (bounds.top > fence.top) { + dy += fence.top - bounds.top; + } + if (bounds.bottom < fence.bottom) { + dy += fence.bottom - bounds.bottom; + } + return [newX + dx, newY + dy]; + } + + /** + * Make a clone, copying any run-time properties. + * If we've hit the global clone limit, returns null. + * @return {RenderedTarget} New clone. + */ + makeClone () { + if (!this.runtime.clonesAvailable() || this.isStage) { + return null; // Hit max clone limit, or this is the stage. + } + this.runtime.changeCloneCounter(1); + const newClone = this.sprite.createClone(); + // Copy all properties. + newClone.x = this.x; + newClone.y = this.y; + newClone.direction = this.direction; + newClone.draggable = this.draggable; + newClone.visible = this.visible; + newClone.size = this.size; + newClone.currentCostume = this.currentCostume; + newClone.rotationStyle = this.rotationStyle; + newClone.effects = JSON.parse(JSON.stringify(this.effects)); + newClone.variables = JSON.parse(JSON.stringify(this.variables)); + newClone.lists = JSON.parse(JSON.stringify(this.lists)); + newClone.initDrawable(); + newClone.updateAllDrawableProperties(); + // Place behind the current target. + newClone.goBehindOther(this); + return newClone; + } + + /** + * Called when the project receives a "green flag." + * For a rendered target, this clears graphic effects. + */ + onGreenFlag () { + this.clearEffects(); + } + + /** + * Called when the project receives a "stop all" + * Stop all sounds and clear graphic effects. + */ + onStopAll () { + this.clearEffects(); + if (this.audioPlayer) { + this.audioPlayer.stopAllSounds(); + this.audioPlayer.clearEffects(); + } + } + + /** + * Post/edit sprite info. + * @param {object} data An object with sprite info data to set. + */ + postSpriteInfo (data) { + const force = data.hasOwnProperty('force') ? data.force : null; + if (data.hasOwnProperty('x')) { + this.setXY(data.x, this.y, force); + } + if (data.hasOwnProperty('y')) { + this.setXY(this.x, data.y, force); + } + if (data.hasOwnProperty('direction')) { + this.setDirection(data.direction); + } + if (data.hasOwnProperty('draggable')) { + this.setDraggable(data.draggable); + } + if (data.hasOwnProperty('rotationStyle')) { + this.setRotationStyle(data.rotationStyle); + } + if (data.hasOwnProperty('visible')) { + this.setVisible(data.visible); + } + } + + /** + * Put the sprite into the drag state. While in effect, setXY must be forced + */ + startDrag () { + this.dragging = true; + } + + /** + * Remove the sprite from the drag state. + */ + stopDrag () { + this.dragging = false; + } + + /** + * Serialize sprite info, used when emitting events about the sprite + * @returns {object} sprite data as a simple object + */ + toJSON () { + return { + id: this.id, + name: this.getName(), + isStage: this.isStage, + x: this.x, + y: this.y, + size: this.size, + direction: this.direction, + draggable: this.draggable, + costume: this.getCurrentCostume(), + costumes: this.getCostumes(), + sounds: this.getSounds(), + costumeCount: this.getCostumes().length, + visible: this.visible, + rotationStyle: this.rotationStyle + }; + } + + /** + * Dispose, destroying any run-time properties. + */ + dispose () { + this.runtime.changeCloneCounter(-1); + if (this.renderer && this.drawableID !== null) { + this.renderer.destroyDrawable(this.drawableID); + if (this.visible) { + this.runtime.requestRedraw(); + } + } + } +} module.exports = RenderedTarget; diff --git a/src/sprites/sprite.js b/src/sprites/sprite.js index a6b00c2e..3a0402a1 100644 --- a/src/sprites/sprite.js +++ b/src/sprites/sprite.js @@ -1,61 +1,63 @@ const RenderedTarget = require('./rendered-target'); const Blocks = require('../engine/blocks'); -/** - * Sprite to be used on the Scratch stage. - * All clones of a sprite have shared blocks, shared costumes, shared variables. - * @param {?Blocks} blocks Shared blocks object for all clones of sprite. - * @param {Runtime} runtime Reference to the runtime. - * @constructor - */ -const Sprite = function (blocks, runtime) { - this.runtime = runtime; - if (!blocks) { - // Shared set of blocks for all clones. - blocks = new Blocks(); +class Sprite { + /** + * Sprite to be used on the Scratch stage. + * All clones of a sprite have shared blocks, shared costumes, shared variables. + * @param {?Blocks} blocks Shared blocks object for all clones of sprite. + * @param {Runtime} runtime Reference to the runtime. + * @constructor + */ + constructor (blocks, runtime) { + this.runtime = runtime; + if (!blocks) { + // Shared set of blocks for all clones. + blocks = new Blocks(); + } + this.blocks = blocks; + /** + * Human-readable name for this sprite (and all clones). + * @type {string} + */ + this.name = ''; + /** + * List of costumes for this sprite. + * Each entry is an object, e.g., + * { + * skinId: 1, + * name: "Costume Name", + * bitmapResolution: 2, + * rotationCenterX: 0, + * rotationCenterY: 0 + * } + * @type {Array.} + */ + this.costumes = []; + /** + * List of sounds for this sprite. + */ + this.sounds = []; + /** + * List of clones for this sprite, including the original. + * @type {Array.} + */ + this.clones = []; } - this.blocks = blocks; - /** - * Human-readable name for this sprite (and all clones). - * @type {string} - */ - this.name = ''; - /** - * List of costumes for this sprite. - * Each entry is an object, e.g., - * { - * skinId: 1, - * name: "Costume Name", - * bitmapResolution: 2, - * rotationCenterX: 0, - * rotationCenterY: 0 - * } - * @type {Array.} - */ - this.costumes = []; - /** - * List of sounds for this sprite. - */ - this.sounds = []; - /** - * List of clones for this sprite, including the original. - * @type {Array.} - */ - this.clones = []; -}; -/** - * Create a clone of this sprite. - * @returns {!RenderedTarget} Newly created clone. - */ -Sprite.prototype.createClone = function () { - const newClone = new RenderedTarget(this, this.runtime); - newClone.isOriginal = this.clones.length === 0; - this.clones.push(newClone); - if (newClone.isOriginal) { - newClone.initDrawable(); + /** + * Create a clone of this sprite. + * @returns {!RenderedTarget} Newly created clone. + */ + createClone () { + const newClone = new RenderedTarget(this, this.runtime); + newClone.isOriginal = this.clones.length === 0; + this.clones.push(newClone); + if (newClone.isOriginal) { + newClone.initDrawable(); + } + return newClone; } - return newClone; -}; +} module.exports = Sprite; diff --git a/src/util/cast.js b/src/util/cast.js index 0d4f26db..c000ee1d 100644 --- a/src/util/cast.js +++ b/src/util/cast.js @@ -1,7 +1,5 @@ const Color = require('../util/color'); -const Cast = function () {}; - /** * @fileoverview * Utilities for casting and comparing Scratch data-types. @@ -13,175 +11,183 @@ const Cast = function () {}; * Use when coercing a value before computation. */ -/** - * Scratch cast to number. - * Treats NaN as 0. - * In Scratch 2.0, this is captured by `interp.numArg.` - * @param {*} value Value to cast to number. - * @return {number} The Scratch-casted number value. - */ -Cast.toNumber = function (value) { - const n = Number(value); - if (isNaN(n)) { - // Scratch treats NaN as 0, when needed as a number. - // E.g., 0 + NaN -> 0. - return 0; - } - return n; -}; - -/** - * Scratch cast to boolean. - * In Scratch 2.0, this is captured by `interp.boolArg.` - * Treats some string values differently from JavaScript. - * @param {*} value Value to cast to boolean. - * @return {boolean} The Scratch-casted boolean value. - */ -Cast.toBoolean = function (value) { - // Already a boolean? - if (typeof value === 'boolean') { - return value; - } - if (typeof value === 'string') { - // These specific strings are treated as false in Scratch. - if ((value === '') || - (value === '0') || - (value.toLowerCase() === 'false')) { - return false; +class Cast { + /** + * Scratch cast to number. + * Treats NaN as 0. + * In Scratch 2.0, this is captured by `interp.numArg.` + * @param {*} value Value to cast to number. + * @return {number} The Scratch-casted number value. + */ + static toNumber (value) { + const n = Number(value); + if (isNaN(n)) { + // Scratch treats NaN as 0, when needed as a number. + // E.g., 0 + NaN -> 0. + return 0; } - // All other strings treated as true. - return true; + return n; } - // Coerce other values and numbers. - return Boolean(value); -}; -/** - * Scratch cast to string. - * @param {*} value Value to cast to string. - * @return {string} The Scratch-casted string value. - */ -Cast.toString = function (value) { - return String(value); -}; - -/** - * Cast any Scratch argument to an RGB color array to be used for the renderer. - * @param {*} value Value to convert to RGB color array. - * @return {Array.} [r,g,b], values between 0-255. - */ -Cast.toRgbColorList = function (value) { - const color = Cast.toRgbColorObject(value); - return [color.r, color.g, color.b]; -}; - -/** - * Cast any Scratch argument to an RGB color object to be used for the renderer. - * @param {*} value Value to convert to RGB color object. - * @return {RGBOject} [r,g,b], values between 0-255. - */ -Cast.toRgbColorObject = function (value) { - let color; - if (typeof value === 'string' && value.substring(0, 1) === '#') { - color = Color.hexToRgb(value); - } else { - color = Color.decimalToRgb(Cast.toNumber(value)); - } - return color; -}; - -/** - * Determine if a Scratch argument is a white space string (or null / empty). - * @param {*} val value to check. - * @return {boolean} True if the argument is all white spaces or null / empty. - */ -Cast.isWhiteSpace = function (val) { - return val === null || typeof val === 'string' && val.trim().length === 0; -}; - -/** - * Compare two values, using Scratch cast, case-insensitive string compare, etc. - * In Scratch 2.0, this is captured by `interp.compare.` - * @param {*} v1 First value to compare. - * @param {*} v2 Second value to compare. - * @returns {number} Negative number if v1 < v2; 0 if equal; positive otherwise. - */ -Cast.compare = function (v1, v2) { - let n1 = Number(v1); - let n2 = Number(v2); - if (n1 === 0 && Cast.isWhiteSpace(v1)) { - n1 = NaN; - } else if (n2 === 0 && Cast.isWhiteSpace(v2)) { - n2 = NaN; - } - if (isNaN(n1) || isNaN(n2)) { - // At least one argument can't be converted to a number. - // Scratch compares strings as case insensitive. - const s1 = String(v1).toLowerCase(); - const s2 = String(v2).toLowerCase(); - return s1.localeCompare(s2); - } - // Compare as numbers. - return n1 - n2; - -}; - -/** - * Determine if a Scratch argument number represents a round integer. - * @param {*} val Value to check. - * @return {boolean} True if number looks like an integer. - */ -Cast.isInt = function (val) { - // Values that are already numbers. - if (typeof val === 'number') { - if (isNaN(val)) { // NaN is considered an integer. + /** + * Scratch cast to boolean. + * In Scratch 2.0, this is captured by `interp.boolArg.` + * Treats some string values differently from JavaScript. + * @param {*} value Value to cast to boolean. + * @return {boolean} The Scratch-casted boolean value. + */ + static toBoolean (value) { + // Already a boolean? + if (typeof value === 'boolean') { + return value; + } + if (typeof value === 'string') { + // These specific strings are treated as false in Scratch. + if ((value === '') || + (value === '0') || + (value.toLowerCase() === 'false')) { + return false; + } + // All other strings treated as true. return true; } - // True if it's "round" (e.g., 2.0 and 2). - return val === parseInt(val, 10); - } else if (typeof val === 'boolean') { - // `True` and `false` always represent integer after Scratch cast. - return true; - } else if (typeof val === 'string') { - // If it contains a decimal point, don't consider it an int. - return val.indexOf('.') < 0; + // Coerce other values and numbers. + return Boolean(value); } - return false; -}; -Cast.LIST_INVALID = 'INVALID'; -Cast.LIST_ALL = 'ALL'; -/** - * Compute a 1-based index into a list, based on a Scratch argument. - * Two special cases may be returned: - * LIST_ALL: if the block is referring to all of the items in the list. - * LIST_INVALID: if the index was invalid in any way. - * @param {*} index Scratch arg, including 1-based numbers or special cases. - * @param {number} length Length of the list. - * @return {(number|string)} 1-based index for list, LIST_ALL, or LIST_INVALID. - */ -Cast.toListIndex = function (index, length) { - if (typeof index !== 'number') { - if (index === 'all') { - return Cast.LIST_ALL; + /** + * Scratch cast to string. + * @param {*} value Value to cast to string. + * @return {string} The Scratch-casted string value. + */ + static toString (value) { + return String(value); + } + + /** + * Cast any Scratch argument to an RGB color array to be used for the renderer. + * @param {*} value Value to convert to RGB color array. + * @return {Array.} [r,g,b], values between 0-255. + */ + static toRgbColorList (value) { + const color = Cast.toRgbColorObject(value); + return [color.r, color.g, color.b]; + } + + /** + * Cast any Scratch argument to an RGB color object to be used for the renderer. + * @param {*} value Value to convert to RGB color object. + * @return {RGBOject} [r,g,b], values between 0-255. + */ + static toRgbColorObject (value) { + let color; + if (typeof value === 'string' && value.substring(0, 1) === '#') { + color = Color.hexToRgb(value); + } else { + color = Color.decimalToRgb(Cast.toNumber(value)); } - if (index === 'last') { - if (length > 0) { - return length; + return color; + } + + /** + * Determine if a Scratch argument is a white space string (or null / empty). + * @param {*} val value to check. + * @return {boolean} True if the argument is all white spaces or null / empty. + */ + static isWhiteSpace (val) { + return val === null || typeof val === 'string' && val.trim().length === 0; + } + + /** + * Compare two values, using Scratch cast, case-insensitive string compare, etc. + * In Scratch 2.0, this is captured by `interp.compare.` + * @param {*} v1 First value to compare. + * @param {*} v2 Second value to compare. + * @returns {number} Negative number if v1 < v2; 0 if equal; positive otherwise. + */ + static compare (v1, v2) { + let n1 = Number(v1); + let n2 = Number(v2); + if (n1 === 0 && Cast.isWhiteSpace(v1)) { + n1 = NaN; + } else if (n2 === 0 && Cast.isWhiteSpace(v2)) { + n2 = NaN; + } + if (isNaN(n1) || isNaN(n2)) { + // At least one argument can't be converted to a number. + // Scratch compares strings as case insensitive. + const s1 = String(v1).toLowerCase(); + const s2 = String(v2).toLowerCase(); + return s1.localeCompare(s2); + } + // Compare as numbers. + return n1 - n2; + + } + + /** + * Determine if a Scratch argument number represents a round integer. + * @param {*} val Value to check. + * @return {boolean} True if number looks like an integer. + */ + static isInt (val) { + // Values that are already numbers. + if (typeof val === 'number') { + if (isNaN(val)) { // NaN is considered an integer. + return true; } - return Cast.LIST_INVALID; - } else if (index === 'random' || index === 'any') { - if (length > 0) { - return 1 + Math.floor(Math.random() * length); + // True if it's "round" (e.g., 2.0 and 2). + return val === parseInt(val, 10); + } else if (typeof val === 'boolean') { + // `True` and `false` always represent integer after Scratch cast. + return true; + } else if (typeof val === 'string') { + // If it contains a decimal point, don't consider it an int. + return val.indexOf('.') < 0; + } + return false; + } + + static get LIST_INVALID () { + return 'INVALID'; + } + + static get LIST_ALL () { + return 'ALL'; + } + + /** + * Compute a 1-based index into a list, based on a Scratch argument. + * Two special cases may be returned: + * LIST_ALL: if the block is referring to all of the items in the list. + * LIST_INVALID: if the index was invalid in any way. + * @param {*} index Scratch arg, including 1-based numbers or special cases. + * @param {number} length Length of the list. + * @return {(number|string)} 1-based index for list, LIST_ALL, or LIST_INVALID. + */ + static toListIndex (index, length) { + if (typeof index !== 'number') { + if (index === 'all') { + return Cast.LIST_ALL; } + if (index === 'last') { + if (length > 0) { + return length; + } + return Cast.LIST_INVALID; + } else if (index === 'random' || index === 'any') { + if (length > 0) { + return 1 + Math.floor(Math.random() * length); + } + return Cast.LIST_INVALID; + } + } + index = Math.floor(Cast.toNumber(index)); + if (index < 1 || index > length) { return Cast.LIST_INVALID; } + return index; } - index = Math.floor(Cast.toNumber(index)); - if (index < 1 || index > length) { - return Cast.LIST_INVALID; - } - return index; -}; +} module.exports = Cast; diff --git a/src/util/clone.js b/src/util/clone.js index 035cadc6..8473e115 100644 --- a/src/util/clone.js +++ b/src/util/clone.js @@ -2,16 +2,16 @@ * Methods for cloning JavaScript objects. * @type {object} */ -const Clone = {}; - -/** - * Deep-clone a "simple" object: one which can be fully expressed with JSON. - * Non-JSON values, such as functions, will be stripped from the clone. - * @param {object} original - the object to be cloned. - * @returns {object} a deep clone of the original object. - */ -Clone.simple = function (original) { - return JSON.parse(JSON.stringify(original)); -}; +class Clone { + /** + * Deep-clone a "simple" object: one which can be fully expressed with JSON. + * Non-JSON values, such as functions, will be stripped from the clone. + * @param {object} original - the object to be cloned. + * @returns {object} a deep clone of the original object. + */ + static simple (original) { + return JSON.parse(JSON.stringify(original)); + } +} module.exports = Clone; diff --git a/src/util/color.js b/src/util/color.js index 1229e0ea..0e1bd228 100644 --- a/src/util/color.js +++ b/src/util/color.js @@ -1,200 +1,204 @@ -const Color = function () {}; +class Color { + /** + * @typedef {object} RGBObject - An object representing a color in RGB format. + * @property {number} r - the red component, in the range [0, 255]. + * @property {number} g - the green component, in the range [0, 255]. + * @property {number} b - the blue component, in the range [0, 255]. + */ -/** - * @typedef {object} RGBObject - An object representing a color in RGB format. - * @property {number} r - the red component, in the range [0, 255]. - * @property {number} g - the green component, in the range [0, 255]. - * @property {number} b - the blue component, in the range [0, 255]. - */ + /** + * @typedef {object} HSVObject - An object representing a color in HSV format. + * @property {number} h - hue, in the range [0-359). + * @property {number} s - saturation, in the range [0,1]. + * @property {number} v - value, in the range [0,1]. + */ -/** - * @typedef {object} HSVObject - An object representing a color in HSV format. - * @property {number} h - hue, in the range [0-359). - * @property {number} s - saturation, in the range [0,1]. - * @property {number} v - value, in the range [0,1]. - */ - -/** @type {RGBObject} */ -Color.RGB_BLACK = {r: 0, g: 0, b: 0}; - -/** @type {RGBObject} */ -Color.RGB_WHITE = {r: 255, g: 255, b: 255}; - -/** - * Convert a Scratch decimal color to a hex string, #RRGGBB. - * @param {number} decimal RGB color as a decimal. - * @return {string} RGB color as #RRGGBB hex string. - */ -Color.decimalToHex = function (decimal) { - if (decimal < 0) { - decimal += 0xFFFFFF + 1; - } - let hex = Number(decimal).toString(16); - hex = `#${'000000'.substr(0, 6 - hex.length)}${hex}`; - return hex; -}; - -/** - * Convert a Scratch decimal color to an RGB color object. - * @param {number} decimal RGB color as decimal. - * @return {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}. - */ -Color.decimalToRgb = function (decimal) { - const a = (decimal >> 24) & 0xFF; - const r = (decimal >> 16) & 0xFF; - const g = (decimal >> 8) & 0xFF; - const b = decimal & 0xFF; - return {r: r, g: g, b: b, a: a > 0 ? a : 255}; -}; - -/** - * Convert a hex color (e.g., F00, #03F, #0033FF) to an RGB color object. - * CC-BY-SA Tim Down: - * https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb - * @param {!string} hex Hex representation of the color. - * @return {RGBObject} null on failure, or rgb: {r: red [0,255], g: green [0,255], b: blue [0,255]}. - */ -Color.hexToRgb = function (hex) { - const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; - hex = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b); - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16) - } : null; -}; - -/** - * Convert an RGB color object to a hex color. - * @param {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}. - * @return {!string} Hex representation of the color. - */ -Color.rgbToHex = function (rgb) { - return Color.decimalToHex(Color.rgbToDecimal(rgb)); -}; - -/** - * Convert an RGB color object to a Scratch decimal color. - * @param {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}. - * @return {!number} Number representing the color. - */ -Color.rgbToDecimal = function (rgb) { - return (rgb.r << 16) + (rgb.g << 8) + rgb.b; -}; - -/** -* Convert a hex color (e.g., F00, #03F, #0033FF) to a decimal color number. -* @param {!string} hex Hex representation of the color. -* @return {!number} Number representing the color. -*/ -Color.hexToDecimal = function (hex) { - return Color.rgbToDecimal(Color.hexToRgb(hex)); -}; - -/** - * Convert an HSV color to RGB format. - * @param {HSVObject} hsv - {h: hue [0,360), s: saturation [0,1], v: value [0,1]} - * @return {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}. - */ -Color.hsvToRgb = function (hsv) { - let h = hsv.h % 360; - if (h < 0) h += 360; - const s = Math.max(0, Math.min(hsv.s, 1)); - const v = Math.max(0, Math.min(hsv.v, 1)); - - const i = Math.floor(h / 60); - const f = (h / 60) - i; - const p = v * (1 - s); - const q = v * (1 - (s * f)); - const t = v * (1 - (s * (1 - f))); - - let r; - let g; - let b; - - switch (i) { - default: - case 0: - r = v; - g = t; - b = p; - break; - case 1: - r = q; - g = v; - b = p; - break; - case 2: - r = p; - g = v; - b = t; - break; - case 3: - r = p; - g = q; - b = v; - break; - case 4: - r = t; - g = p; - b = v; - break; - case 5: - r = v; - g = p; - b = q; - break; + /** @type {RGBObject} */ + static get RGB_BLACK () { + return {r: 0, g: 0, b: 0}; } - return { - r: Math.floor(r * 255), - g: Math.floor(g * 255), - b: Math.floor(b * 255) - }; -}; - -/** - * Convert an RGB color to HSV format. - * @param {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}. - * @return {HSVObject} hsv - {h: hue [0,360), s: saturation [0,1], v: value [0,1]} - */ -Color.rgbToHsv = function (rgb) { - const r = rgb.r / 255; - const g = rgb.g / 255; - const b = rgb.b / 255; - const x = Math.min(Math.min(r, g), b); - const v = Math.max(Math.max(r, g), b); - - // For grays, hue will be arbitrarily reported as zero. Otherwise, calculate - let h = 0; - let s = 0; - if (x !== v) { - const f = (r === x) ? g - b : ((g === x) ? b - r : r - g); - const i = (r === x) ? 3 : ((g === x) ? 5 : 1); - h = ((i - (f / (v - x))) * 60) % 360; - s = (v - x) / v; + /** @type {RGBObject} */ + static get RGB_WHITE () { + return {r: 255, g: 255, b: 255}; } - return {h: h, s: s, v: v}; -}; + /** + * Convert a Scratch decimal color to a hex string, #RRGGBB. + * @param {number} decimal RGB color as a decimal. + * @return {string} RGB color as #RRGGBB hex string. + */ + static decimalToHex (decimal) { + if (decimal < 0) { + decimal += 0xFFFFFF + 1; + } + let hex = Number(decimal).toString(16); + hex = `#${'000000'.substr(0, 6 - hex.length)}${hex}`; + return hex; + } -/** - * Linear interpolation between rgb0 and rgb1. - * @param {RGBObject} rgb0 - the color corresponding to fraction1 <= 0. - * @param {RGBObject} rgb1 - the color corresponding to fraction1 >= 1. - * @param {number} fraction1 - the interpolation parameter. If this is 0.5, for example, mix the two colors equally. - * @return {RGBObject} the interpolated color. - */ -Color.mixRgb = function (rgb0, rgb1, fraction1) { - if (fraction1 <= 0) return rgb0; - if (fraction1 >= 1) return rgb1; - const fraction0 = 1 - fraction1; - return { - r: (fraction0 * rgb0.r) + (fraction1 * rgb1.r), - g: (fraction0 * rgb0.g) + (fraction1 * rgb1.g), - b: (fraction0 * rgb0.b) + (fraction1 * rgb1.b) - }; -}; + /** + * Convert a Scratch decimal color to an RGB color object. + * @param {number} decimal RGB color as decimal. + * @return {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}. + */ + static decimalToRgb (decimal) { + const a = (decimal >> 24) & 0xFF; + const r = (decimal >> 16) & 0xFF; + const g = (decimal >> 8) & 0xFF; + const b = decimal & 0xFF; + return {r: r, g: g, b: b, a: a > 0 ? a : 255}; + } + + /** + * Convert a hex color (e.g., F00, #03F, #0033FF) to an RGB color object. + * CC-BY-SA Tim Down: + * https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb + * @param {!string} hex Hex representation of the color. + * @return {RGBObject} null on failure, or rgb: {r: red [0,255], g: green [0,255], b: blue [0,255]}. + */ + static hexToRgb (hex) { + const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + hex = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b); + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16) + } : null; + } + + /** + * Convert an RGB color object to a hex color. + * @param {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}. + * @return {!string} Hex representation of the color. + */ + static rgbToHex (rgb) { + return Color.decimalToHex(Color.rgbToDecimal(rgb)); + } + + /** + * Convert an RGB color object to a Scratch decimal color. + * @param {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}. + * @return {!number} Number representing the color. + */ + static rgbToDecimal (rgb) { + return (rgb.r << 16) + (rgb.g << 8) + rgb.b; + } + + /** + * Convert a hex color (e.g., F00, #03F, #0033FF) to a decimal color number. + * @param {!string} hex Hex representation of the color. + * @return {!number} Number representing the color. + */ + static hexToDecimal (hex) { + return Color.rgbToDecimal(Color.hexToRgb(hex)); + } + + /** + * Convert an HSV color to RGB format. + * @param {HSVObject} hsv - {h: hue [0,360), s: saturation [0,1], v: value [0,1]} + * @return {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}. + */ + static hsvToRgb (hsv) { + let h = hsv.h % 360; + if (h < 0) h += 360; + const s = Math.max(0, Math.min(hsv.s, 1)); + const v = Math.max(0, Math.min(hsv.v, 1)); + + const i = Math.floor(h / 60); + const f = (h / 60) - i; + const p = v * (1 - s); + const q = v * (1 - (s * f)); + const t = v * (1 - (s * (1 - f))); + + let r; + let g; + let b; + + switch (i) { + default: + case 0: + r = v; + g = t; + b = p; + break; + case 1: + r = q; + g = v; + b = p; + break; + case 2: + r = p; + g = v; + b = t; + break; + case 3: + r = p; + g = q; + b = v; + break; + case 4: + r = t; + g = p; + b = v; + break; + case 5: + r = v; + g = p; + b = q; + break; + } + + return { + r: Math.floor(r * 255), + g: Math.floor(g * 255), + b: Math.floor(b * 255) + }; + } + + /** + * Convert an RGB color to HSV format. + * @param {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}. + * @return {HSVObject} hsv - {h: hue [0,360), s: saturation [0,1], v: value [0,1]} + */ + static rgbToHsv (rgb) { + const r = rgb.r / 255; + const g = rgb.g / 255; + const b = rgb.b / 255; + const x = Math.min(Math.min(r, g), b); + const v = Math.max(Math.max(r, g), b); + + // For grays, hue will be arbitrarily reported as zero. Otherwise, calculate + let h = 0; + let s = 0; + if (x !== v) { + const f = (r === x) ? g - b : ((g === x) ? b - r : r - g); + const i = (r === x) ? 3 : ((g === x) ? 5 : 1); + h = ((i - (f / (v - x))) * 60) % 360; + s = (v - x) / v; + } + + return {h: h, s: s, v: v}; + } + + /** + * Linear interpolation between rgb0 and rgb1. + * @param {RGBObject} rgb0 - the color corresponding to fraction1 <= 0. + * @param {RGBObject} rgb1 - the color corresponding to fraction1 >= 1. + * @param {number} fraction1 - the interpolation parameter. If this is 0.5, for example, mix the two colors equally. + * @return {RGBObject} the interpolated color. + */ + static mixRgb (rgb0, rgb1, fraction1) { + if (fraction1 <= 0) return rgb0; + if (fraction1 >= 1) return rgb1; + const fraction0 = 1 - fraction1; + return { + r: (fraction0 * rgb0.r) + (fraction1 * rgb1.r), + g: (fraction0 * rgb0.g) + (fraction1 * rgb1.g), + b: (fraction0 * rgb0.b) + (fraction1 * rgb1.b) + }; + } +} module.exports = Color; diff --git a/src/util/math-util.js b/src/util/math-util.js index b92452b1..542f1bd4 100644 --- a/src/util/math-util.js +++ b/src/util/math-util.js @@ -1,68 +1,68 @@ -const MathUtil = function () {}; - -/** - * Convert a value from degrees to radians. - * @param {!number} deg Value in degrees. - * @return {!number} Equivalent value in radians. - */ -MathUtil.degToRad = function (deg) { - return deg * Math.PI / 180; -}; - -/** - * Convert a value from radians to degrees. - * @param {!number} rad Value in radians. - * @return {!number} Equivalent value in degrees. - */ -MathUtil.radToDeg = function (rad) { - return rad * 180 / Math.PI; -}; - -/** - * Clamp a number between two limits. - * If n < min, return min. If n > max, return max. Else, return n. - * @param {!number} n Number to clamp. - * @param {!number} min Minimum limit. - * @param {!number} max Maximum limit. - * @return {!number} Value of n clamped to min and max. - */ -MathUtil.clamp = function (n, min, max) { - return Math.min(Math.max(n, min), max); -}; - -/** - * Keep a number between two limits, wrapping "extra" into the range. - * e.g., wrapClamp(7, 1, 5) == 2 - * wrapClamp(0, 1, 5) == 5 - * wrapClamp(-11, -10, 6) == 6, etc. - * @param {!number} n Number to wrap. - * @param {!number} min Minimum limit. - * @param {!number} max Maximum limit. - * @return {!number} Value of n wrapped between min and max. - */ -MathUtil.wrapClamp = function (n, min, max) { - const range = (max - min) + 1; - return n - (Math.floor((n - min) / range) * range); -}; - - -/** - * Convert a value from tan function in degrees. - * @param {!number} angle in degrees - * @return {!number} Correct tan value - */ -MathUtil.tan = function (angle) { - angle = angle % 360; - switch (angle) { - case -270: - case 90: - return Infinity; - case -90: - case 270: - return -Infinity; - default: - return parseFloat(Math.tan((Math.PI * angle) / 180).toFixed(10)); +class MathUtil { + /** + * Convert a value from degrees to radians. + * @param {!number} deg Value in degrees. + * @return {!number} Equivalent value in radians. + */ + static degToRad (deg) { + return deg * Math.PI / 180; } -}; + + /** + * Convert a value from radians to degrees. + * @param {!number} rad Value in radians. + * @return {!number} Equivalent value in degrees. + */ + static radToDeg (rad) { + return rad * 180 / Math.PI; + } + + /** + * Clamp a number between two limits. + * If n < min, return min. If n > max, return max. Else, return n. + * @param {!number} n Number to clamp. + * @param {!number} min Minimum limit. + * @param {!number} max Maximum limit. + * @return {!number} Value of n clamped to min and max. + */ + static clamp (n, min, max) { + return Math.min(Math.max(n, min), max); + } + + /** + * Keep a number between two limits, wrapping "extra" into the range. + * e.g., wrapClamp(7, 1, 5) == 2 + * wrapClamp(0, 1, 5) == 5 + * wrapClamp(-11, -10, 6) == 6, etc. + * @param {!number} n Number to wrap. + * @param {!number} min Minimum limit. + * @param {!number} max Maximum limit. + * @return {!number} Value of n wrapped between min and max. + */ + static wrapClamp (n, min, max) { + const range = (max - min) + 1; + return n - (Math.floor((n - min) / range) * range); + } + + + /** + * Convert a value from tan function in degrees. + * @param {!number} angle in degrees + * @return {!number} Correct tan value + */ + static tan (angle) { + angle = angle % 360; + switch (angle) { + case -270: + case 90: + return Infinity; + case -90: + case 270: + return -Infinity; + default: + return parseFloat(Math.tan((Math.PI * angle) / 180).toFixed(10)); + } + } +} module.exports = MathUtil; diff --git a/src/util/string-util.js b/src/util/string-util.js index ed40a2e6..0ee71254 100644 --- a/src/util/string-util.js +++ b/src/util/string-util.js @@ -1,17 +1,17 @@ -const StringUtil = function () {}; +class StringUtil { + static withoutTrailingDigits (s) { + let i = s.length - 1; + while ((i >= 0) && ('0123456789'.indexOf(s.charAt(i)) > -1)) i--; + return s.slice(0, i + 1); + } -StringUtil.withoutTrailingDigits = function (s) { - let i = s.length - 1; - while ((i >= 0) && ('0123456789'.indexOf(s.charAt(i)) > -1)) i--; - return s.slice(0, i + 1); -}; - -StringUtil.unusedName = function (name, existingNames) { - if (existingNames.indexOf(name) < 0) return name; - name = StringUtil.withoutTrailingDigits(name); - let i = 2; - while (existingNames.indexOf(name + i) >= 0) i++; - return name + i; -}; + static unusedName (name, existingNames) { + if (existingNames.indexOf(name) < 0) return name; + name = StringUtil.withoutTrailingDigits(name); + let i = 2; + while (existingNames.indexOf(name + i) >= 0) i++; + return name + i; + } +} module.exports = StringUtil; diff --git a/src/util/timer.js b/src/util/timer.js index 79a1852b..8ad026e7 100644 --- a/src/util/timer.js +++ b/src/util/timer.js @@ -12,69 +12,78 @@ * to do some measurement yourself. */ -/** - * @constructor - */ -const Timer = function () {}; - -/** - * Used to store the start time of a timer action. - * Updated when calling `timer.start`. - */ -Timer.prototype.startTime = 0; - -/** - * Disable use of self.performance for now as it results in lower performance - * However, instancing it like below (caching the self.performance to a local variable) negates most of the issues. - * @type {boolean} - */ -const USE_PERFORMANCE = false; - -/** - * Legacy object to allow for us to call now to get the old style date time (for backwards compatibility) - * @deprecated This is only called via the nowObj.now() if no other means is possible... - */ -const legacyDateCode = { - now: function () { - return new Date().getTime(); +class Timer { + constructor () { + /** + * Used to store the start time of a timer action. + * Updated when calling `timer.start`. + */ + this.startTime = 0; } -}; -/** - * Use this object to route all time functions through single access points. - */ -const nowObj = (USE_PERFORMANCE && typeof self !== 'undefined' && self.performance && 'now' in self.performance) ? - self.performance : Date.now ? Date : legacyDateCode; + /** + * Disable use of self.performance for now as it results in lower performance + * However, instancing it like below (caching the self.performance to a local variable) negates most of the issues. + * @type {boolean} + */ + static get USE_PERFORMANCE () { + return false; + } -/** - * Return the currently known absolute time, in ms precision. - * @returns {number} ms elapsed since 1 January 1970 00:00:00 UTC. - */ -Timer.prototype.time = function () { - return nowObj.now(); -}; + /** + * Legacy object to allow for us to call now to get the old style date time (for backwards compatibility) + * @deprecated This is only called via the nowObj.now() if no other means is possible... + */ + static get legacyDateCode () { + return { + now: function () { + return new Date().getTime(); + } + }; + } -/** - * Returns a time accurate relative to other times produced by this function. - * If possible, will use sub-millisecond precision. - * If not, will use millisecond precision. - * Not guaranteed to produce the same absolute values per-system. - * @returns {number} ms-scale accurate time relative to other relative times. - */ -Timer.prototype.relativeTime = function () { - return nowObj.now(); -}; + /** + * Use this object to route all time functions through single access points. + */ + static get nowObj () { + if (Timer.USE_PERFORMANCE && typeof self !== 'undefined' && self.performance && 'now' in self.performance) { + return self.performance; + } else if (Date.now) { + return Date; + } + return Timer.legacyDateCode; + } -/** - * Start a timer for measuring elapsed time, - * at the most accurate precision possible. - */ -Timer.prototype.start = function () { - this.startTime = nowObj.now(); -}; + /** + * Return the currently known absolute time, in ms precision. + * @returns {number} ms elapsed since 1 January 1970 00:00:00 UTC. + */ + time () { + return Timer.nowObj.now(); + } -Timer.prototype.timeElapsed = function () { - return nowObj.now() - this.startTime; -}; + /** + * Returns a time accurate relative to other times produced by this function. + * If possible, will use sub-millisecond precision. + * If not, will use millisecond precision. + * Not guaranteed to produce the same absolute values per-system. + * @returns {number} ms-scale accurate time relative to other relative times. + */ + relativeTime () { + return Timer.nowObj.now(); + } + + /** + * Start a timer for measuring elapsed time, + * at the most accurate precision possible. + */ + start () { + this.startTime = Timer.nowObj.now(); + } + + timeElapsed () { + return Timer.nowObj.now() - this.startTime; + } +} module.exports = Timer; diff --git a/src/virtual-machine.js b/src/virtual-machine.js index bc2cc493..c530ad43 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -1,5 +1,4 @@ const EventEmitter = require('events'); -const util = require('util'); const log = require('./util/log'); const Runtime = require('./engine/runtime'); @@ -18,416 +17,413 @@ const AssetType = ScratchStorage.AssetType; * Handles connections between blocks, stage, and extensions. * @constructor */ -const VirtualMachine = function () { - const instance = this; - // Bind event emitter and runtime to VM instance - EventEmitter.call(instance); +class VirtualMachine extends EventEmitter { + constructor () { + super(); + + const instance = this; + /** + * VM runtime, to store blocks, I/O devices, sprites/targets, etc. + * @type {!Runtime} + */ + instance.runtime = new Runtime(); + /** + * The "currently editing"/selected target ID for the VM. + * Block events from any Blockly workspace are routed to this target. + * @type {!string} + */ + instance.editingTarget = null; + // Runtime emits are passed along as VM emits. + instance.runtime.on(Runtime.SCRIPT_GLOW_ON, glowData => { + instance.emit(Runtime.SCRIPT_GLOW_ON, glowData); + }); + instance.runtime.on(Runtime.SCRIPT_GLOW_OFF, glowData => { + instance.emit(Runtime.SCRIPT_GLOW_OFF, glowData); + }); + instance.runtime.on(Runtime.BLOCK_GLOW_ON, glowData => { + instance.emit(Runtime.BLOCK_GLOW_ON, glowData); + }); + instance.runtime.on(Runtime.BLOCK_GLOW_OFF, glowData => { + instance.emit(Runtime.BLOCK_GLOW_OFF, glowData); + }); + instance.runtime.on(Runtime.PROJECT_RUN_START, () => { + instance.emit(Runtime.PROJECT_RUN_START); + }); + instance.runtime.on(Runtime.PROJECT_RUN_STOP, () => { + instance.emit(Runtime.PROJECT_RUN_STOP); + }); + instance.runtime.on(Runtime.VISUAL_REPORT, visualReport => { + instance.emit(Runtime.VISUAL_REPORT, visualReport); + }); + instance.runtime.on(Runtime.SPRITE_INFO_REPORT, spriteInfo => { + instance.emit(Runtime.SPRITE_INFO_REPORT, spriteInfo); + }); + + this.blockListener = this.blockListener.bind(this); + this.flyoutBlockListener = this.flyoutBlockListener.bind(this); + } + /** - * VM runtime, to store blocks, I/O devices, sprites/targets, etc. - * @type {!Runtime} + * Start running the VM - do this before anything else. */ - instance.runtime = new Runtime(); + start () { + this.runtime.start(); + } + /** - * The "currently editing"/selected target ID for the VM. - * Block events from any Blockly workspace are routed to this target. - * @type {!string} + * "Green flag" handler - start all threads starting with a green flag. */ - instance.editingTarget = null; - // Runtime emits are passed along as VM emits. - instance.runtime.on(Runtime.SCRIPT_GLOW_ON, glowData => { - instance.emit(Runtime.SCRIPT_GLOW_ON, glowData); - }); - instance.runtime.on(Runtime.SCRIPT_GLOW_OFF, glowData => { - instance.emit(Runtime.SCRIPT_GLOW_OFF, glowData); - }); - instance.runtime.on(Runtime.BLOCK_GLOW_ON, glowData => { - instance.emit(Runtime.BLOCK_GLOW_ON, glowData); - }); - instance.runtime.on(Runtime.BLOCK_GLOW_OFF, glowData => { - instance.emit(Runtime.BLOCK_GLOW_OFF, glowData); - }); - instance.runtime.on(Runtime.PROJECT_RUN_START, () => { - instance.emit(Runtime.PROJECT_RUN_START); - }); - instance.runtime.on(Runtime.PROJECT_RUN_STOP, () => { - instance.emit(Runtime.PROJECT_RUN_STOP); - }); - instance.runtime.on(Runtime.VISUAL_REPORT, visualReport => { - instance.emit(Runtime.VISUAL_REPORT, visualReport); - }); - instance.runtime.on(Runtime.SPRITE_INFO_REPORT, spriteInfo => { - instance.emit(Runtime.SPRITE_INFO_REPORT, spriteInfo); - }); - - this.blockListener = this.blockListener.bind(this); - this.flyoutBlockListener = this.flyoutBlockListener.bind(this); -}; - -/** - * Inherit from EventEmitter - */ -util.inherits(VirtualMachine, EventEmitter); - -/** - * Start running the VM - do this before anything else. - */ -VirtualMachine.prototype.start = function () { - this.runtime.start(); -}; - -/** - * "Green flag" handler - start all threads starting with a green flag. - */ -VirtualMachine.prototype.greenFlag = function () { - this.runtime.greenFlag(); -}; - -/** - * Set whether the VM is in "turbo mode." - * When true, loops don't yield to redraw. - * @param {boolean} turboModeOn Whether turbo mode should be set. - */ -VirtualMachine.prototype.setTurboMode = function (turboModeOn) { - this.runtime.turboMode = !!turboModeOn; -}; - -/** - * Set whether the VM is in 2.0 "compatibility mode." - * When true, ticks go at 2.0 speed (30 TPS). - * @param {boolean} compatibilityModeOn Whether compatibility mode is set. - */ -VirtualMachine.prototype.setCompatibilityMode = function (compatibilityModeOn) { - this.runtime.setCompatibilityMode(!!compatibilityModeOn); -}; - -/** - * Stop all threads and running activities. - */ -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. - */ -VirtualMachine.prototype.getPlaygroundData = function () { - const instance = this; - // Only send back thread data for the current editingTarget. - const threadData = this.runtime.threads.filter(thread => thread.target === instance.editingTarget); - // Remove the target key, since it's a circular reference. - const filteredThreadData = JSON.stringify(threadData, (key, value) => { - if (key === 'target') return; - return value; - }, 2); - this.emit('playgroundData', { - blocks: this.editingTarget.blocks, - threads: filteredThreadData - }); -}; - -/** - * Post I/O data to the virtual devices. - * @param {?string} device Name of virtual I/O device. - * @param {object} data Any data object to post to the I/O device. - */ -VirtualMachine.prototype.postIOData = function (device, data) { - if (this.runtime.ioDevices[device]) { - this.runtime.ioDevices[device].postData(data); + greenFlag () { + this.runtime.greenFlag(); } -}; -/** - * Load a project from a Scratch 2.0 JSON representation. - * @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 first sprite. - this.editingTarget = this.runtime.targets[1]; - // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); -}; - -/** - * Load a project from the Scratch web site, by ID. - * @param {string} id - the ID of the project to download, as a string. - */ -VirtualMachine.prototype.downloadProjectId = function (id) { - if (!this.runtime.storage) { - log.error('No storage module present; cannot load project: ', id); - return; + /** + * Set whether the VM is in "turbo mode." + * When true, loops don't yield to redraw. + * @param {boolean} turboModeOn Whether turbo mode should be set. + */ + setTurboMode (turboModeOn) { + this.runtime.turboMode = !!turboModeOn; } - const vm = this; - const promise = this.runtime.storage.load(AssetType.Project, id); - promise.then(projectAsset => { - vm.loadProject(projectAsset.decodeText()); - }); -}; -/** - * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. - * @param {string} json JSON string representing the sprite. - */ -VirtualMachine.prototype.addSprite2 = function (json) { - // Select new sprite. - this.editingTarget = sb2import(json, this.runtime, true); - // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); -}; + /** + * Set whether the VM is in 2.0 "compatibility mode." + * When true, ticks go at 2.0 speed (30 TPS). + * @param {boolean} compatibilityModeOn Whether compatibility mode is set. + */ + setCompatibilityMode (compatibilityModeOn) { + this.runtime.setCompatibilityMode(!!compatibilityModeOn); + } -/** - * Add a costume to the current editing target. - * @param {string} md5ext - the MD5 and extension of the costume to be loaded. - * @param {!object} costumeObject Object representing the costume. - * @property {int} skinId - the ID of the costume's render skin, once installed. - * @property {number} rotationCenterX - the X component of the costume's origin. - * @property {number} rotationCenterY - the Y component of the costume's origin. - * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume. - */ -VirtualMachine.prototype.addCostume = function (md5ext, costumeObject) { - loadCostume(md5ext, costumeObject, this.runtime).then(() => { - this.editingTarget.sprite.costumes.push(costumeObject); - this.editingTarget.setCostume( - this.editingTarget.sprite.costumes.length - 1 - ); - }); -}; + /** + * Stop all threads and running activities. + */ + stopAll () { + this.runtime.stopAll(); + } -/** - * Add a sound to the current editing target. - * @param {!object} soundObject Object representing the costume. - * @returns {?Promise} - a promise that resolves when the sound has been decoded and added - */ -VirtualMachine.prototype.addSound = function (soundObject) { - return loadSound(soundObject, this.runtime).then(() => { - this.editingTarget.sprite.sounds.push(soundObject); + /** + * Clear out current running project data. + */ + clear () { + this.runtime.dispose(); + this.editingTarget = null; this.emitTargetsUpdate(); - }); -}; - -/** - * Add a backdrop to the stage. - * @param {string} md5ext - the MD5 and extension of the backdrop to be loaded. - * @param {!object} backdropObject Object representing the backdrop. - * @property {int} skinId - the ID of the backdrop's render skin, once installed. - * @property {number} rotationCenterX - the X component of the backdrop's origin. - * @property {number} rotationCenterY - the Y component of the backdrop's origin. - * @property {number} [bitmapResolution] - the resolution scale for a bitmap backdrop. - */ -VirtualMachine.prototype.addBackdrop = function (md5ext, backdropObject) { - loadCostume(md5ext, backdropObject, this.runtime).then(() => { - const stage = this.runtime.getTargetForStage(); - stage.sprite.costumes.push(backdropObject); - stage.setCostume(stage.sprite.costumes.length - 1); - }); -}; - -/** - * Rename a sprite. - * @param {string} targetId ID of a target whose sprite to rename. - * @param {string} newName New name of the sprite. - */ -VirtualMachine.prototype.renameSprite = function (targetId, newName) { - const target = this.runtime.getTargetById(targetId); - if (target) { - if (!target.isSprite()) { - throw new Error('Cannot rename non-sprite targets.'); - } - const sprite = target.sprite; - if (!sprite) { - throw new Error('No sprite associated with this target.'); - } - if (newName && RESERVED_NAMES.indexOf(newName) === -1) { - const names = this.runtime.targets - .filter(runtimeTarget => runtimeTarget.isSprite()) - .map(runtimeTarget => runtimeTarget.sprite.name); - - sprite.name = StringUtil.unusedName(newName, names); - } - this.emitTargetsUpdate(); - } else { - throw new Error('No target with the provided id.'); } -}; -/** - * Delete a sprite and all its clones. - * @param {string} targetId ID of a target whose sprite to delete. - */ -VirtualMachine.prototype.deleteSprite = function (targetId) { - const target = this.runtime.getTargetById(targetId); - if (target) { - if (!target.isSprite()) { - throw new Error('Cannot delete non-sprite targets.'); - } - const sprite = target.sprite; - if (!sprite) { - throw new Error('No sprite associated with this target.'); - } - const currentEditingTarget = this.editingTarget; - for (let i = 0; i < sprite.clones.length; i++) { - const clone = sprite.clones[i]; - this.runtime.stopForTarget(sprite.clones[i]); - this.runtime.disposeTarget(sprite.clones[i]); - // Ensure editing target is switched if we are deleting it. - if (clone === currentEditingTarget) { - this.setEditingTarget(this.runtime.targets[0].id); - } - } - // Sprite object should be deleted by GC. - this.emitTargetsUpdate(); - } else { - throw new Error('No target with the provided id.'); + /** + * Get data for playground. Data comes back in an emitted event. + */ + getPlaygroundData () { + const instance = this; + // Only send back thread data for the current editingTarget. + const threadData = this.runtime.threads.filter(thread => thread.target === instance.editingTarget); + // Remove the target key, since it's a circular reference. + const filteredThreadData = JSON.stringify(threadData, (key, value) => { + if (key === 'target') return; + return value; + }, 2); + this.emit('playgroundData', { + blocks: this.editingTarget.blocks, + threads: filteredThreadData + }); } -}; -/** - * Set the audio engine for the VM/runtime - * @param {!AudioEngine} audioEngine The audio engine to attach - */ -VirtualMachine.prototype.attachAudioEngine = function (audioEngine) { - this.runtime.attachAudioEngine(audioEngine); -}; - -/** - * Set the renderer for the VM/runtime - * @param {!RenderWebGL} renderer The renderer to attach - */ -VirtualMachine.prototype.attachRenderer = function (renderer) { - this.runtime.attachRenderer(renderer); -}; - -/** - * Set the storage module for the VM/runtime - * @param {!ScratchStorage} storage The storage module to attach - */ -VirtualMachine.prototype.attachStorage = function (storage) { - this.runtime.attachStorage(storage); -}; - -/** - * Handle a Blockly event for the current editing target. - * @param {!Blockly.Event} e Any Blockly event. - */ -VirtualMachine.prototype.blockListener = function (e) { - if (this.editingTarget) { - this.editingTarget.blocks.blocklyListen(e, this.runtime); + /** + * Post I/O data to the virtual devices. + * @param {?string} device Name of virtual I/O device. + * @param {object} data Any data object to post to the I/O device. + */ + postIOData (device, data) { + if (this.runtime.ioDevices[device]) { + this.runtime.ioDevices[device].postData(data); + } } -}; -/** - * 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. - * After switching the editing target, the VM may emit updates - * to the list of targets and any attached workspace blocks - * (see `emitTargetsUpdate` and `emitWorkspaceUpdate`). - * @param {string} targetId Id of target to set as editing. - */ -VirtualMachine.prototype.setEditingTarget = function (targetId) { - // Has the target id changed? If not, exit. - if (targetId === this.editingTarget.id) { - return; - } - const target = this.runtime.getTargetById(targetId); - if (target) { - this.editingTarget = target; - // Emit appropriate UI updates. + /** + * Load a project from a Scratch 2.0 JSON representation. + * @param {?string} json JSON string representing the project. + */ + loadProject (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 first sprite. + this.editingTarget = this.runtime.targets[1]; + // Update the VM user's knowledge of targets and blocks on the workspace. this.emitTargetsUpdate(); this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(target); + this.runtime.setEditingTarget(this.editingTarget); } -}; -/** - * Emit metadata about available targets. - * An editor UI could use this to display a list of targets and show - * the currently editing one. - */ -VirtualMachine.prototype.emitTargetsUpdate = function () { - this.emit('targetsUpdate', { - // [[target id, human readable target name], ...]. - targetList: this.runtime.targets.filter(target => - // Don't report clones. - !target.hasOwnProperty('isOriginal') || target.isOriginal).map(target => target.toJSON()), - // Currently editing target id. - editingTarget: this.editingTarget ? this.editingTarget.id : null - }); -}; - -/** - * Emit an Blockly/scratch-blocks compatible XML representation - * of the current editing target's blocks. - */ -VirtualMachine.prototype.emitWorkspaceUpdate = function () { - this.emit('workspaceUpdate', { - xml: this.editingTarget.blocks.toXML() - }); -}; - -/** - * Get a target id for a drawable id. Useful for interacting with the renderer - * @param {int} drawableId The drawable id to request the target id for - * @returns {?string} The target id, if found. Will also be null if the target found is the stage. - */ -VirtualMachine.prototype.getTargetIdForDrawableId = function (drawableId) { - const target = this.runtime.getTargetByDrawableId(drawableId); - if (target && target.hasOwnProperty('id') && target.hasOwnProperty('isStage') && !target.isStage) { - return target.id; + /** + * Load a project from the Scratch web site, by ID. + * @param {string} id - the ID of the project to download, as a string. + */ + downloadProjectId (id) { + if (!this.runtime.storage) { + log.error('No storage module present; cannot load project: ', id); + return; + } + const vm = this; + const promise = this.runtime.storage.load(AssetType.Project, id); + promise.then(projectAsset => { + vm.loadProject(projectAsset.decodeText()); + }); } - return null; -}; -/** - * Put a target into a "drag" state, during which its X/Y positions will be unaffected - * by blocks. - * @param {string} targetId The id for the target to put into a drag state - */ -VirtualMachine.prototype.startDrag = function (targetId) { - const target = this.runtime.getTargetById(targetId); - if (target) { - target.startDrag(); - this.setEditingTarget(target.id); + /** + * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. + * @param {string} json JSON string representing the sprite. + */ + addSprite2 (json) { + // Select new sprite. + this.editingTarget = sb2import(json, this.runtime, true); + // Update the VM user's knowledge of targets and blocks on the workspace. + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); } -}; -/** - * Remove a target from a drag state, so blocks may begin affecting X/Y position again - * @param {string} targetId The id for the target to remove from the drag state - */ -VirtualMachine.prototype.stopDrag = function (targetId) { - const target = this.runtime.getTargetById(targetId); - if (target) target.stopDrag(); -}; + /** + * Add a costume to the current editing target. + * @param {string} md5ext - the MD5 and extension of the costume to be loaded. + * @param {!object} costumeObject Object representing the costume. + * @property {int} skinId - the ID of the costume's render skin, once installed. + * @property {number} rotationCenterX - the X component of the costume's origin. + * @property {number} rotationCenterY - the Y component of the costume's origin. + * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume. + */ + addCostume (md5ext, costumeObject) { + loadCostume(md5ext, costumeObject, this.runtime).then(() => { + this.editingTarget.sprite.costumes.push(costumeObject); + this.editingTarget.setCostume( + this.editingTarget.sprite.costumes.length - 1 + ); + }); + } -/** - * Post/edit sprite info for the current editing target. - * @param {object} data An object with sprite info data to set. - */ -VirtualMachine.prototype.postSpriteInfo = function (data) { - this.editingTarget.postSpriteInfo(data); -}; + /** + * Add a sound to the current editing target. + * @param {!object} soundObject Object representing the costume. + * @returns {?Promise} - a promise that resolves when the sound has been decoded and added + */ + addSound (soundObject) { + return loadSound(soundObject, this.runtime).then(() => { + this.editingTarget.sprite.sounds.push(soundObject); + this.emitTargetsUpdate(); + }); + } + + /** + * Add a backdrop to the stage. + * @param {string} md5ext - the MD5 and extension of the backdrop to be loaded. + * @param {!object} backdropObject Object representing the backdrop. + * @property {int} skinId - the ID of the backdrop's render skin, once installed. + * @property {number} rotationCenterX - the X component of the backdrop's origin. + * @property {number} rotationCenterY - the Y component of the backdrop's origin. + * @property {number} [bitmapResolution] - the resolution scale for a bitmap backdrop. + */ + addBackdrop (md5ext, backdropObject) { + loadCostume(md5ext, backdropObject, this.runtime).then(() => { + const stage = this.runtime.getTargetForStage(); + stage.sprite.costumes.push(backdropObject); + stage.setCostume(stage.sprite.costumes.length - 1); + }); + } + + /** + * Rename a sprite. + * @param {string} targetId ID of a target whose sprite to rename. + * @param {string} newName New name of the sprite. + */ + renameSprite (targetId, newName) { + const target = this.runtime.getTargetById(targetId); + if (target) { + if (!target.isSprite()) { + throw new Error('Cannot rename non-sprite targets.'); + } + const sprite = target.sprite; + if (!sprite) { + throw new Error('No sprite associated with this target.'); + } + if (newName && RESERVED_NAMES.indexOf(newName) === -1) { + const names = this.runtime.targets + .filter(runtimeTarget => runtimeTarget.isSprite()) + .map(runtimeTarget => runtimeTarget.sprite.name); + + sprite.name = StringUtil.unusedName(newName, names); + } + this.emitTargetsUpdate(); + } else { + throw new Error('No target with the provided id.'); + } + } + + /** + * Delete a sprite and all its clones. + * @param {string} targetId ID of a target whose sprite to delete. + */ + deleteSprite (targetId) { + const target = this.runtime.getTargetById(targetId); + if (target) { + if (!target.isSprite()) { + throw new Error('Cannot delete non-sprite targets.'); + } + const sprite = target.sprite; + if (!sprite) { + throw new Error('No sprite associated with this target.'); + } + const currentEditingTarget = this.editingTarget; + for (let i = 0; i < sprite.clones.length; i++) { + const clone = sprite.clones[i]; + this.runtime.stopForTarget(sprite.clones[i]); + this.runtime.disposeTarget(sprite.clones[i]); + // Ensure editing target is switched if we are deleting it. + if (clone === currentEditingTarget) { + this.setEditingTarget(this.runtime.targets[0].id); + } + } + // Sprite object should be deleted by GC. + this.emitTargetsUpdate(); + } else { + throw new Error('No target with the provided id.'); + } + } + + /** + * Set the audio engine for the VM/runtime + * @param {!AudioEngine} audioEngine The audio engine to attach + */ + attachAudioEngine (audioEngine) { + this.runtime.attachAudioEngine(audioEngine); + } + + /** + * Set the renderer for the VM/runtime + * @param {!RenderWebGL} renderer The renderer to attach + */ + attachRenderer (renderer) { + this.runtime.attachRenderer(renderer); + } + + /** + * Set the storage module for the VM/runtime + * @param {!ScratchStorage} storage The storage module to attach + */ + attachStorage (storage) { + this.runtime.attachStorage(storage); + } + + /** + * Handle a Blockly event for the current editing target. + * @param {!Blockly.Event} e Any Blockly event. + */ + blockListener (e) { + if (this.editingTarget) { + this.editingTarget.blocks.blocklyListen(e, this.runtime); + } + } + + /** + * Handle a Blockly event for the flyout. + * @param {!Blockly.Event} e Any Blockly event. + */ + flyoutBlockListener (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. + * After switching the editing target, the VM may emit updates + * to the list of targets and any attached workspace blocks + * (see `emitTargetsUpdate` and `emitWorkspaceUpdate`). + * @param {string} targetId Id of target to set as editing. + */ + setEditingTarget (targetId) { + // Has the target id changed? If not, exit. + if (targetId === this.editingTarget.id) { + return; + } + const target = this.runtime.getTargetById(targetId); + if (target) { + this.editingTarget = target; + // Emit appropriate UI updates. + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(target); + } + } + + /** + * Emit metadata about available targets. + * An editor UI could use this to display a list of targets and show + * the currently editing one. + */ + emitTargetsUpdate () { + this.emit('targetsUpdate', { + // [[target id, human readable target name], ...]. + targetList: this.runtime.targets.filter(target => + // Don't report clones. + !target.hasOwnProperty('isOriginal') || target.isOriginal).map(target => target.toJSON()), + // Currently editing target id. + editingTarget: this.editingTarget ? this.editingTarget.id : null + }); + } + + /** + * Emit an Blockly/scratch-blocks compatible XML representation + * of the current editing target's blocks. + */ + emitWorkspaceUpdate () { + this.emit('workspaceUpdate', { + xml: this.editingTarget.blocks.toXML() + }); + } + + /** + * Get a target id for a drawable id. Useful for interacting with the renderer + * @param {int} drawableId The drawable id to request the target id for + * @returns {?string} The target id, if found. Will also be null if the target found is the stage. + */ + getTargetIdForDrawableId (drawableId) { + const target = this.runtime.getTargetByDrawableId(drawableId); + if (target && target.hasOwnProperty('id') && target.hasOwnProperty('isStage') && !target.isStage) { + return target.id; + } + return null; + } + + /** + * Put a target into a "drag" state, during which its X/Y positions will be unaffected + * by blocks. + * @param {string} targetId The id for the target to put into a drag state + */ + startDrag (targetId) { + const target = this.runtime.getTargetById(targetId); + if (target) { + target.startDrag(); + this.setEditingTarget(target.id); + } + } + + /** + * Remove a target from a drag state, so blocks may begin affecting X/Y position again + * @param {string} targetId The id for the target to remove from the drag state + */ + stopDrag (targetId) { + const target = this.runtime.getTargetById(targetId); + if (target) target.stopDrag(); + } + + /** + * Post/edit sprite info for the current editing target. + * @param {object} data An object with sprite info data to set. + */ + postSpriteInfo (data) { + this.editingTarget.postSpriteInfo(data); + } +} module.exports = VirtualMachine; From 9be29439b184396557ddc1537846e0efc85b7f77 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 17 Apr 2017 19:55:34 -0400 Subject: [PATCH 294/721] Fix webpack for ES6 --- package.json | 3 +++ webpack.config.js | 18 ++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index e39cd199..1904e174 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,10 @@ }, "devDependencies": { "adm-zip": "0.4.7", + "babel-core": "^6.24.1", "babel-eslint": "^7.1.1", + "babel-loader": "^6.4.1", + "babel-preset-es2015": "^6.24.1", "copy-webpack-plugin": "4.0.1", "eslint": "^3.16.0", "eslint-config-scratch": "^3.1.0", diff --git a/webpack.config.js b/webpack.config.js index 0fe8af04..401b8bde 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -10,6 +10,16 @@ var base = { port: process.env.PORT || 8073 }, devtool: 'source-map', + module: { + rules: [{ + test: /\.js$/, + loader: 'babel-loader', + include: path.resolve(__dirname, 'src'), + query: { + presets: ['es2015'] + } + }] + }, plugins: [ new webpack.optimize.UglifyJsPlugin({ include: /\.min\.js$/, @@ -31,12 +41,12 @@ module.exports = [ filename: '[name].js' }, module: { - rules: [ + rules: base.module.rules.concat([ { test: require.resolve('./src/index.js'), loader: 'expose-loader?VirtualMachine' } - ] + ]) } }), // Node-compatible @@ -77,7 +87,7 @@ module.exports = [ filename: '[name].js' }, module: { - loaders: [ + rules: base.module.rules.concat([ { test: require.resolve('./src/index.js'), loader: 'expose-loader?VirtualMachine' @@ -106,7 +116,7 @@ module.exports = [ test: require.resolve('scratch-storage'), loader: 'expose-loader?Scratch.Storage' } - ] + ]) }, plugins: base.plugins.concat([ new CopyWebpackPlugin([{ From 7dc9fad41e09ea74da6c309ddf880ae716f231b7 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 17 Apr 2017 20:03:28 -0400 Subject: [PATCH 295/721] Update to Node 6 LTS --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3b44f442..29c45a80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: node_js node_js: -- "4" -- "node" +- 6 +- node sudo: false cache: directories: From d86fe6a075cc9ee570b75054cbe4ae43a5f135a2 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 18 Apr 2017 09:27:58 -0400 Subject: [PATCH 296/721] Do not add targets to runtime until they are fully loaded --- src/import/load-sound.js | 6 ++++-- src/import/sb2import.js | 34 ++++++++++++++++++++++++---------- src/virtual-machine.js | 37 ++++++++++++++++++++++++------------- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/src/import/load-sound.js b/src/import/load-sound.js index 74856326..e01f0b4d 100644 --- a/src/import/load-sound.js +++ b/src/import/load-sound.js @@ -7,7 +7,7 @@ var log = require('../util/log'); * @property {string} md5 - the MD5 and extension of the sound to be loaded. * @property {Buffer} data - sound data will be written here once loaded. * @param {!Runtime} runtime - Scratch runtime, used to access the storage module. - * @returns {!Promise} - a promise which will resolve after sound is loaded + * @returns {!Promise} - a promise which will resolves to the sound when ready. */ var loadSound = function (sound, runtime) { if (!runtime.storage) { @@ -22,7 +22,9 @@ var loadSound = function (sound, runtime) { var md5 = idParts[0]; return runtime.storage.load(AssetType.Sound, md5).then(function (soundAsset) { sound.data = soundAsset.data; - return runtime.audioEngine.decodeSound(sound); + return runtime.audioEngine.decodeSound(sound).then(function () { + return sound; + }); }); }; diff --git a/src/import/sb2import.js b/src/import/sb2import.js index ac905d33..5988f2af 100644 --- a/src/import/sb2import.js +++ b/src/import/sb2import.js @@ -23,7 +23,7 @@ var loadSound = require('./load-sound.js'); * @param {!object} object From-JSON "Scratch object:" sprite, stage, watcher. * @param {!Runtime} runtime Runtime object to load all structures into. * @param {boolean} topLevel Whether this is the top-level object (stage). - * @return {?Target} Target created (stage or sprite). + * @return {?Promise} Promise that resolves to the loaded targets when ready. */ var parseScratchObject = function (object, runtime, topLevel) { if (!object.hasOwnProperty('objName')) { @@ -51,11 +51,11 @@ var parseScratchObject = function (object, runtime, topLevel) { rotationCenterY: costumeSource.rotationCenterY, skinId: null }; - sprite.costumes.push(costume); costumePromises.push(loadCostume(costumeSource.baseLayerMD5, costume, runtime)); } } // Sounds from JSON + var soundPromises = []; if (object.hasOwnProperty('sounds')) { for (var s = 0; s < object.sounds.length; s++) { var soundSource = object.sounds[s]; @@ -68,8 +68,7 @@ var parseScratchObject = function (object, runtime, topLevel) { md5: soundSource.md5, data: null }; - loadSound(sound, runtime); - sprite.sounds.push(sound); + soundPromises.push(loadSound(sound, runtime)); } } // If included, parse any and all scripts/blocks on the object. @@ -78,8 +77,7 @@ var parseScratchObject = function (object, runtime, topLevel) { } // Create the first clone, and load its run-state from JSON. var target = sprite.createClone(); - // Add it to the runtime's list of targets. - runtime.targets.push(target); + // Load target properties from JSON. if (object.hasOwnProperty('variables')) { for (var j = 0; j < object.variables.length; j++) { @@ -132,18 +130,34 @@ var parseScratchObject = function (object, runtime, topLevel) { target.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND; } } + target.isStage = topLevel; + Promise.all(costumePromises).then(function (costumes) { sprite.costumes = costumes; - target.updateAllDrawableProperties(); }); + + Promise.all(soundPromises).then(function (sounds) { + sprite.sounds = sounds; + }); + // The stage will have child objects; recursively process them. + var childrenPromises = []; if (object.children) { for (var m = 0; m < object.children.length; m++) { - parseScratchObject(object.children[m], runtime, false); + childrenPromises.push(parseScratchObject(object.children[m], runtime, false)); } } - return target; + + return Promise.all(costumePromises.concat(soundPromises)).then(function () { + return Promise.all(childrenPromises).then(function (children) { + var targets = [target]; + for (var n = 0; n < children.length; n++) { + targets = targets.concat(children[n]); + } + return targets; + }); + }); }; /** @@ -152,7 +166,7 @@ var parseScratchObject = function (object, runtime, topLevel) { * @param {!string} json SB2-format JSON to load. * @param {!Runtime} runtime Runtime object to load all structures into. * @param {boolean=} optForceSprite If set, treat as sprite (Sprite2). - * @return {?Target} Top-level target created (stage or sprite). + * @return {?Promise} Promise that resolves to the loaded targets when ready. */ var sb2import = function (json, runtime, optForceSprite) { return parseScratchObject( diff --git a/src/virtual-machine.js b/src/virtual-machine.js index b85705e3..16876de4 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -152,15 +152,22 @@ VirtualMachine.prototype.postIOData = function (device, data) { * @param {?string} json JSON string representing the project. */ VirtualMachine.prototype.loadProject = function (json) { - this.clear(); + var that = this; // @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 first sprite. - this.editingTarget = this.runtime.targets[1]; - // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); + sb2import(json, this.runtime).then(function (targets) { + that.clear(); + for (var n = 0; n < targets.length; n++) { + that.runtime.targets.push(targets[n]); + that.runtime.targets[n].updateAllDrawableProperties(); + } + // Select the first target for editing, e.g., the first sprite. + that.editingTarget = that.runtime.targets[1]; + + // Update the VM user's knowledge of targets and blocks on the workspace. + that.emitTargetsUpdate(); + that.emitWorkspaceUpdate(); + that.runtime.setEditingTarget(that.editingTarget); + }); }; /** @@ -185,11 +192,15 @@ VirtualMachine.prototype.downloadProjectId = function (id) { */ VirtualMachine.prototype.addSprite2 = function (json) { // Select new sprite. - this.editingTarget = sb2import(json, this.runtime, true); - // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); + var that = this; + sb2import(json, this.runtime, true).then(function (targets) { + that.runtime.targets.push(targets[0]); + that.editingTarget = targets[0]; + // Update the VM user's knowledge of targets and blocks on the workspace. + that.emitTargetsUpdate(); + that.emitWorkspaceUpdate(); + that.runtime.setEditingTarget(that.editingTarget); + }); }; /** From f161640876c1daaef8cad448c6f78cbf1e62b250 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 18 Apr 2017 11:55:38 -0400 Subject: [PATCH 297/721] Update tests --- src/import/sb2import.js | 2 +- src/virtual-machine.js | 9 ++-- test/integration/complex.js | 62 +++++++++++++------------ test/integration/control.js | 5 +- test/integration/data.js | 17 +++---- test/integration/event.js | 17 +++---- test/integration/hat-execution-order.js | 17 +++---- test/integration/import_sb2.js | 50 ++++++++++---------- test/integration/looks.js | 17 +++---- test/integration/motion.js | 17 +++---- test/integration/pen.js | 17 +++---- test/integration/procedure.js | 17 +++---- test/integration/sensing.js | 17 +++---- test/integration/sound.js | 17 +++---- 14 files changed, 148 insertions(+), 133 deletions(-) diff --git a/src/import/sb2import.js b/src/import/sb2import.js index 5988f2af..081ac74e 100644 --- a/src/import/sb2import.js +++ b/src/import/sb2import.js @@ -29,7 +29,7 @@ var parseScratchObject = function (object, runtime, topLevel) { if (!object.hasOwnProperty('objName')) { // Watcher/monitor - skip this object until those are implemented in VM. // @todo - return null; + return Promise.resolve(null); } // Blocks container for this object. var blocks = new Blocks(); diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 16876de4..f5b25b90 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -150,15 +150,18 @@ VirtualMachine.prototype.postIOData = function (device, data) { /** * Load a project from a Scratch 2.0 JSON representation. * @param {?string} json JSON string representing the project. + * @return {!Promise} Promise that resolves after targets are installed. */ VirtualMachine.prototype.loadProject = function (json) { var that = this; // @todo: Handle other formats, e.g., Scratch 1.4, Scratch 3.0. - sb2import(json, this.runtime).then(function (targets) { + return sb2import(json, this.runtime).then(function (targets) { that.clear(); for (var n = 0; n < targets.length; n++) { - that.runtime.targets.push(targets[n]); - that.runtime.targets[n].updateAllDrawableProperties(); + if (targets[n] !== null) { + that.runtime.targets.push(targets[n]); + targets[n].updateAllDrawableProperties(); + } } // Select the first target for editing, e.g., the first sprite. that.editingTarget = that.runtime.targets[1]; diff --git a/test/integration/complex.js b/test/integration/complex.js index 831f40f0..b8777fe1 100644 --- a/test/integration/complex.js +++ b/test/integration/complex.js @@ -60,38 +60,40 @@ test('complex', function (t) { vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project); - vm.greenFlag(); + vm.loadProject(project).then(function () { + vm.greenFlag(); - // Post IO data - vm.postIOData('mouse', { - isDown: true, - x: 0, - y: 10, - canvasWidth: 100, - canvasHeight: 100 + // Post IO data + vm.postIOData('mouse', { + isDown: true, + x: 0, + y: 10, + canvasWidth: 100, + canvasHeight: 100 + }); + + // Add sprite + vm.addSprite2(sprite); + + // Add backdrop + vm.addBackdrop( + '6b3d87ba2a7f89be703163b6c1d4c964.png', + { + costumeName: 'baseball-field', + baseLayerID: 26, + baseLayerMD5: '6b3d87ba2a7f89be703163b6c1d4c964.png', + bitmapResolution: 2, + rotationCenterX: 480, + rotationCenterY: 360 + } + ); + + // After two seconds, get playground data and stop + setTimeout(function () { + vm.getPlaygroundData(); + vm.stopAll(); + }, 2000); }); - - // Add sprite - vm.addSprite2(sprite); - - // Add backdrop - vm.addBackdrop( - '6b3d87ba2a7f89be703163b6c1d4c964.png', - { - costumeName: 'baseball-field', - baseLayerID: 26, - baseLayerMD5: '6b3d87ba2a7f89be703163b6c1d4c964.png', - bitmapResolution: 2, - rotationCenterX: 480, - rotationCenterY: 360 - } - ); }); - // After two seconds, get playground data and stop - setTimeout(function () { - vm.getPlaygroundData(); - vm.stopAll(); - }, 2000); }); diff --git a/test/integration/control.js b/test/integration/control.js index ad5d4682..0d0dbfae 100644 --- a/test/integration/control.js +++ b/test/integration/control.js @@ -25,8 +25,9 @@ test('control', function (t) { vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project); - vm.greenFlag(); + vm.loadProject(project).then(function () { + vm.greenFlag(); + }); }); // After two seconds, get playground data and stop diff --git a/test/integration/data.js b/test/integration/data.js index e41e40ad..52b528c5 100644 --- a/test/integration/data.js +++ b/test/integration/data.js @@ -24,13 +24,14 @@ test('data', function (t) { vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project); - vm.greenFlag(); - }); + vm.loadProject(project).then(function () { + vm.greenFlag(); - // After two seconds, get playground data and stop - setTimeout(function () { - vm.getPlaygroundData(); - vm.stopAll(); - }, 2000); + // After two seconds, get playground data and stop + setTimeout(function () { + vm.getPlaygroundData(); + vm.stopAll(); + }, 2000); + }); + }); }); diff --git a/test/integration/event.js b/test/integration/event.js index dfa814b6..f404b2b5 100644 --- a/test/integration/event.js +++ b/test/integration/event.js @@ -25,13 +25,14 @@ test('event', function (t) { vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project); - vm.greenFlag(); - }); + vm.loadProject(project).then(function () { + vm.greenFlag(); - // After two seconds, get playground data and stop - setTimeout(function () { - vm.getPlaygroundData(); - vm.stopAll(); - }, 2000); + // After two seconds, get playground data and stop + setTimeout(function () { + vm.getPlaygroundData(); + vm.stopAll(); + }, 2000); + }); + }); }); diff --git a/test/integration/hat-execution-order.js b/test/integration/hat-execution-order.js index df5798a7..8a04d942 100644 --- a/test/integration/hat-execution-order.js +++ b/test/integration/hat-execution-order.js @@ -29,13 +29,14 @@ test('complex', function (t) { vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project); - vm.greenFlag(); - }); + vm.loadProject(project).then(function () { + vm.greenFlag(); - // After two seconds, get playground data and stop - setTimeout(function () { - vm.getPlaygroundData(); - vm.stopAll(); - }, 2000); + // After two seconds, get playground data and stop + setTimeout(function () { + vm.getPlaygroundData(); + vm.stopAll(); + }, 2000); + }); + }); }); diff --git a/test/integration/import_sb2.js b/test/integration/import_sb2.js index 8ae74d0b..010148cf 100644 --- a/test/integration/import_sb2.js +++ b/test/integration/import_sb2.js @@ -20,33 +20,33 @@ test('default', function (t) { // Create runtime instance & load SB2 into it var rt = new runtime(); attachTestStorage(rt); - sb2(file, rt); + sb2(file, rt).then(function (targets) { + // Test + t.type(file, 'string'); + t.type(rt, 'object'); + t.type(targets, 'object'); - // Test - t.type(file, 'string'); - t.type(rt, 'object'); - t.type(rt.targets, 'object'); + t.ok(targets[0] instanceof renderedTarget); + t.type(targets[0].id, 'string'); + t.type(targets[0].blocks, 'object'); + t.type(targets[0].variables, 'object'); + t.type(targets[0].lists, 'object'); - t.ok(rt.targets[0] instanceof renderedTarget); - 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(targets[0].isOriginal, true); + t.equal(targets[0].currentCostume, 0); + t.equal(targets[0].isOriginal, true); + t.equal(targets[0].isStage, true); - 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(targets[1] instanceof renderedTarget); + t.type(targets[1].id, 'string'); + t.type(targets[1].blocks, 'object'); + t.type(targets[1].variables, 'object'); + t.type(targets[1].lists, 'object'); - t.ok(rt.targets[1] instanceof renderedTarget); - 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(); + t.equal(targets[1].isOriginal, true); + t.equal(targets[1].currentCostume, 0); + t.equal(targets[1].isOriginal, true); + t.equal(targets[1].isStage, false); + t.end(); + }); }); diff --git a/test/integration/looks.js b/test/integration/looks.js index 6da3583c..5f5313e9 100644 --- a/test/integration/looks.js +++ b/test/integration/looks.js @@ -25,13 +25,14 @@ test('looks', function (t) { vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project); - vm.greenFlag(); - }); + vm.loadProject(project).then(function () { + vm.greenFlag(); - // After two seconds, get playground data and stop - setTimeout(function () { - vm.getPlaygroundData(); - vm.stopAll(); - }, 2000); + // After two seconds, get playground data and stop + setTimeout(function () { + vm.getPlaygroundData(); + vm.stopAll(); + }, 2000); + }); + }); }); diff --git a/test/integration/motion.js b/test/integration/motion.js index 623411ea..cbf0880e 100644 --- a/test/integration/motion.js +++ b/test/integration/motion.js @@ -25,13 +25,14 @@ test('motion', function (t) { vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project); - vm.greenFlag(); - }); + vm.loadProject(project).then(function () { + vm.greenFlag(); - // After two seconds, get playground data and stop - setTimeout(function () { - vm.getPlaygroundData(); - vm.stopAll(); - }, 2000); + // After two seconds, get playground data and stop + setTimeout(function () { + vm.getPlaygroundData(); + vm.stopAll(); + }, 2000); + }); + }); }); diff --git a/test/integration/pen.js b/test/integration/pen.js index 5b9ea618..fde8a52c 100644 --- a/test/integration/pen.js +++ b/test/integration/pen.js @@ -24,13 +24,14 @@ test('pen', function (t) { vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project); - vm.greenFlag(); - }); + vm.loadProject(project).then(function () { + vm.greenFlag(); - // After two seconds, get playground data and stop - setTimeout(function () { - vm.getPlaygroundData(); - vm.stopAll(); - }, 2000); + // After two seconds, get playground data and stop + setTimeout(function () { + vm.getPlaygroundData(); + vm.stopAll(); + }, 2000); + }); + }); }); diff --git a/test/integration/procedure.js b/test/integration/procedure.js index 713ba6b0..177b2461 100644 --- a/test/integration/procedure.js +++ b/test/integration/procedure.js @@ -25,13 +25,14 @@ test('procedure', function (t) { vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project); - vm.greenFlag(); - }); + vm.loadProject(project).then(function () { + vm.greenFlag(); - // After two seconds, get playground data and stop - setTimeout(function () { - vm.getPlaygroundData(); - vm.stopAll(); - }, 2000); + // After two seconds, get playground data and stop + setTimeout(function () { + vm.getPlaygroundData(); + vm.stopAll(); + }, 2000); + }); + }); }); diff --git a/test/integration/sensing.js b/test/integration/sensing.js index 3590fdb1..f90d26d0 100644 --- a/test/integration/sensing.js +++ b/test/integration/sensing.js @@ -25,13 +25,14 @@ test('sensing', function (t) { vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project); - vm.greenFlag(); - }); + vm.loadProject(project).then(function () { + vm.greenFlag(); - // After two seconds, get playground data and stop - setTimeout(function () { - vm.getPlaygroundData(); - vm.stopAll(); - }, 2000); + // After two seconds, get playground data and stop + setTimeout(function () { + vm.getPlaygroundData(); + vm.stopAll(); + }, 2000); + }); + }); }); diff --git a/test/integration/sound.js b/test/integration/sound.js index e948d2f4..f1f46419 100644 --- a/test/integration/sound.js +++ b/test/integration/sound.js @@ -25,13 +25,14 @@ test('sound', function (t) { vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project); - vm.greenFlag(); - }); + vm.loadProject(project).then(function () { + vm.greenFlag(); - // After two seconds, get playground data and stop - setTimeout(function () { - vm.getPlaygroundData(); - vm.stopAll(); - }, 2000); + // After two seconds, get playground data and stop + setTimeout(function () { + vm.getPlaygroundData(); + vm.stopAll(); + }, 2000); + }); + }); }); From 4b5e7a57a57aa14bfba6dc0bc2e616a7c519dc44 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 19 Apr 2017 08:53:07 -0400 Subject: [PATCH 298/721] Fix binding issues --- src/import/load-sound.js | 2 +- src/virtual-machine.js | 28 +++++++++++++--------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/import/load-sound.js b/src/import/load-sound.js index e01f0b4d..e50077c4 100644 --- a/src/import/load-sound.js +++ b/src/import/load-sound.js @@ -7,7 +7,7 @@ var log = require('../util/log'); * @property {string} md5 - the MD5 and extension of the sound to be loaded. * @property {Buffer} data - sound data will be written here once loaded. * @param {!Runtime} runtime - Scratch runtime, used to access the storage module. - * @returns {!Promise} - a promise which will resolves to the sound when ready. + * @returns {!Promise} - a promise which will resolve to the sound when ready. */ var loadSound = function (sound, runtime) { if (!runtime.storage) { diff --git a/src/virtual-machine.js b/src/virtual-machine.js index f5b25b90..ca0edc9f 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -153,24 +153,23 @@ VirtualMachine.prototype.postIOData = function (device, data) { * @return {!Promise} Promise that resolves after targets are installed. */ VirtualMachine.prototype.loadProject = function (json) { - var that = this; // @todo: Handle other formats, e.g., Scratch 1.4, Scratch 3.0. return sb2import(json, this.runtime).then(function (targets) { - that.clear(); + this.clear(); for (var n = 0; n < targets.length; n++) { if (targets[n] !== null) { - that.runtime.targets.push(targets[n]); + this.runtime.targets.push(targets[n]); targets[n].updateAllDrawableProperties(); } } // Select the first target for editing, e.g., the first sprite. - that.editingTarget = that.runtime.targets[1]; + this.editingTarget = this.runtime.targets[1]; // Update the VM user's knowledge of targets and blocks on the workspace. - that.emitTargetsUpdate(); - that.emitWorkspaceUpdate(); - that.runtime.setEditingTarget(that.editingTarget); - }); + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); + }.bind(this)); }; /** @@ -195,15 +194,14 @@ VirtualMachine.prototype.downloadProjectId = function (id) { */ VirtualMachine.prototype.addSprite2 = function (json) { // Select new sprite. - var that = this; sb2import(json, this.runtime, true).then(function (targets) { - that.runtime.targets.push(targets[0]); - that.editingTarget = targets[0]; + this.runtime.targets.push(targets[0]); + this.editingTarget = targets[0]; // Update the VM user's knowledge of targets and blocks on the workspace. - that.emitTargetsUpdate(); - that.emitWorkspaceUpdate(); - that.runtime.setEditingTarget(that.editingTarget); - }); + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); + }.bind(this)); }; /** From d2343e3601900c663370d89be7d6c1ba06af3b54 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 19 Apr 2017 17:36:33 -0400 Subject: [PATCH 299/721] Fix up some auto-converted weirdness --- src/engine/blocks.js | 21 +++++++++---------- src/import/load-costume.js | 42 ++++++++++++++++++++------------------ src/virtual-machine.js | 10 ++++++--- 3 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 0b4b1ec4..312c7377 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -402,15 +402,15 @@ class Blocks { const block = this._blocks[blockId]; // Encode properties of this block. const tagName = (block.shadow) ? 'shadow' : 'block'; - const xy = (block.topLevel) ? - ` x="${block.x}" y="${block.y}"` : - ''; - let xmlString = ''; - xmlString += `<${tagName - } id="${block.id}"` + - ` type="${block.opcode}"${ - xy - }>`; + let xmlString = + `<${tagName} + id="${block.id}" + type="${block.opcode}" + ${block.topLevel ? + `x="${block.x} y="${block.y}` : + '' + } + >`; // Add any mutation. Must come before inputs. if (block.mutation) { xmlString += this.mutationToXML(block.mutation); @@ -440,8 +440,7 @@ class Blocks { if (typeof value === 'string') { value = xmlEscape(blockField.value); } - xmlString += `${ - value}`; + xmlString += `${value}`; } // Add blocks connected to the next connection. if (block.next) { diff --git a/src/import/load-costume.js b/src/import/load-costume.js index 487dc741..09c869dd 100644 --- a/src/import/load-costume.js +++ b/src/import/load-costume.js @@ -46,26 +46,28 @@ const loadCostume = function (md5ext, costume, runtime) { return costume; }); } else { - promise = promise.then(costumeAsset => new Promise((resolve, reject) => { - const imageElement = new Image(); - const onError = function () { - // eslint-disable-next-line no-use-before-define - removeEventListeners(); - reject(); - }; - const onLoad = function () { - // eslint-disable-next-line no-use-before-define - removeEventListeners(); - resolve(imageElement); - }; - const removeEventListeners = function () { - imageElement.removeEventListener('error', onError); - imageElement.removeEventListener('load', onLoad); - }; - imageElement.addEventListener('error', onError); - imageElement.addEventListener('load', onLoad); - imageElement.src = costumeAsset.encodeDataURI(); - })).then(imageElement => { + promise = promise.then(costumeAsset => ( + new Promise((resolve, reject) => { + const imageElement = new Image(); + const onError = function () { + // eslint-disable-next-line no-use-before-define + removeEventListeners(); + reject(); + }; + const onLoad = function () { + // eslint-disable-next-line no-use-before-define + removeEventListeners(); + resolve(imageElement); + }; + const removeEventListeners = function () { + imageElement.removeEventListener('error', onError); + imageElement.removeEventListener('load', onLoad); + }; + imageElement.addEventListener('error', onError); + imageElement.addEventListener('load', onLoad); + imageElement.src = costumeAsset.encodeDataURI(); + }) + )).then(imageElement => { costume.skinId = runtime.renderer.createBitmapSkin(imageElement, costume.bitmapResolution, rotationCenter); return costume; }); diff --git a/src/virtual-machine.js b/src/virtual-machine.js index c530ad43..e818168e 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -364,9 +364,13 @@ class VirtualMachine extends EventEmitter { emitTargetsUpdate () { this.emit('targetsUpdate', { // [[target id, human readable target name], ...]. - targetList: this.runtime.targets.filter(target => - // Don't report clones. - !target.hasOwnProperty('isOriginal') || target.isOriginal).map(target => target.toJSON()), + targetList: this.runtime.targets + .filter( + // Don't report clones. + target => !target.hasOwnProperty('isOriginal') || target.isOriginal + ).map( + target => target.toJSON() + ), // Currently editing target id. editingTarget: this.editingTarget ? this.editingTarget.id : null }); From 9c34b692c0302f86d0196feeaa1827cbe81b5d1d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 19 Apr 2017 17:54:52 -0400 Subject: [PATCH 300/721] Fix ES6 lint after merge --- src/import/load-sound.js | 4 +--- src/import/sb2import.js | 28 ++++++++++++++++------------ src/virtual-machine.js | 39 ++++++++++++++++++++------------------- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/import/load-sound.js b/src/import/load-sound.js index 6c9f565d..dddbd492 100644 --- a/src/import/load-sound.js +++ b/src/import/load-sound.js @@ -22,9 +22,7 @@ const loadSound = function (sound, runtime) { const md5 = idParts[0]; return runtime.storage.load(AssetType.Sound, md5).then(soundAsset => { sound.data = soundAsset.data; - return runtime.audioEngine.decodeSound(sound).then(function () { - return sound; - }); + return runtime.audioEngine.decodeSound(sound).then(() => sound); }); }; diff --git a/src/import/sb2import.js b/src/import/sb2import.js index 53729f58..4abd6230 100644 --- a/src/import/sb2import.js +++ b/src/import/sb2import.js @@ -164,7 +164,7 @@ const parseScratchObject = function (object, runtime, topLevel) { } } // Sounds from JSON - var soundPromises = []; + const soundPromises = []; if (object.hasOwnProperty('sounds')) { for (let s = 0; s < object.sounds.length; s++) { const soundSource = object.sounds[s]; @@ -185,7 +185,7 @@ const parseScratchObject = function (object, runtime, topLevel) { parseScripts(object.scripts, blocks); } // Create the first clone, and load its run-state from JSON. - var target = sprite.createClone(); + const target = sprite.createClone(); // Load target properties from JSON. if (object.hasOwnProperty('variables')) { @@ -242,31 +242,35 @@ const parseScratchObject = function (object, runtime, topLevel) { target.isStage = topLevel; - Promise.all(costumePromises).then(function (costumes) { + Promise.all(costumePromises).then(costumes => { sprite.costumes = costumes; }); - Promise.all(soundPromises).then(function (sounds) { + Promise.all(soundPromises).then(sounds => { sprite.sounds = sounds; }); // The stage will have child objects; recursively process them. - var childrenPromises = []; + const childrenPromises = []; if (object.children) { - for (var m = 0; m < object.children.length; m++) { + for (let m = 0; m < object.children.length; m++) { childrenPromises.push(parseScratchObject(object.children[m], runtime, false)); } } - return Promise.all(costumePromises.concat(soundPromises)).then(function () { - return Promise.all(childrenPromises).then(function (children) { - var targets = [target]; - for (var n = 0; n < children.length; n++) { + return Promise.all( + costumePromises.concat(soundPromises) + ).then(() => + Promise.all( + childrenPromises + ).then(children => { + let targets = [target]; + for (let n = 0; n < children.length; n++) { targets = targets.concat(children[n]); } return targets; - }); - }); + }) + ); }; /** diff --git a/src/virtual-machine.js b/src/virtual-machine.js index cad80670..8dfed97a 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -143,25 +143,26 @@ class VirtualMachine extends EventEmitter { /** * Load a project from a Scratch 2.0 JSON representation. * @param {?string} json JSON string representing the project. + * @return {!Promise} Promise that resolves after targets are installed. */ loadProject (json) { // @todo: Handle other formats, e.g., Scratch 1.4, Scratch 3.0. - return sb2import(json, this.runtime).then(function (targets) { - this.clear(); - for (var n = 0; n < targets.length; n++) { - if (targets[n] !== null) { - this.runtime.targets.push(targets[n]); - targets[n].updateAllDrawableProperties(); + return sb2import(json, this.runtime).then(targets => { + this.clear(); + for (let n = 0; n < targets.length; n++) { + if (targets[n] !== null) { + this.runtime.targets.push(targets[n]); + targets[n].updateAllDrawableProperties(); + } } - } // Select the first target for editing, e.g., the first sprite. - this.editingTarget = this.runtime.targets[1]; + this.editingTarget = this.runtime.targets[1]; // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); - }.bind(this)); + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); + }); } /** @@ -186,14 +187,14 @@ class VirtualMachine extends EventEmitter { */ addSprite2 (json) { // Select new sprite. - sb2import(json, this.runtime, true).then(function (targets) { - this.runtime.targets.push(targets[0]); - this.editingTarget = targets[0]; + sb2import(json, this.runtime, true).then(targets => { + this.runtime.targets.push(targets[0]); + this.editingTarget = targets[0]; // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); - }.bind(this)); + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); + }); } /** From c750f53c81ae43b5d6819e7eef4d73e0ff2d75d6 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Thu, 20 Apr 2017 15:30:38 -0700 Subject: [PATCH 301/721] Get `Asset` and `AssetType` from storage instance --- src/import/load-costume.js | 3 +-- src/import/load-sound.js | 3 +-- src/playground/playground.js | 2 +- src/virtual-machine.js | 8 +++----- test/fixtures/attach-test-storage.js | 2 +- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/import/load-costume.js b/src/import/load-costume.js index 09c869dd..5d9fdc1d 100644 --- a/src/import/load-costume.js +++ b/src/import/load-costume.js @@ -1,4 +1,3 @@ -const AssetType = require('scratch-storage').AssetType; const log = require('../util/log'); /** @@ -19,7 +18,7 @@ const loadCostume = function (md5ext, costume, runtime) { return Promise.resolve(costume); } - + const AssetType = runtime.storage.AssetType; const idParts = md5ext.split('.'); const md5 = idParts[0]; const ext = idParts[1].toUpperCase(); diff --git a/src/import/load-sound.js b/src/import/load-sound.js index dddbd492..f54f5fe3 100644 --- a/src/import/load-sound.js +++ b/src/import/load-sound.js @@ -1,4 +1,3 @@ -const AssetType = require('scratch-storage').AssetType; const log = require('../util/log'); /** @@ -20,7 +19,7 @@ const loadSound = function (sound, runtime) { } const idParts = sound.md5.split('.'); const md5 = idParts[0]; - return runtime.storage.load(AssetType.Sound, md5).then(soundAsset => { + return runtime.storage.load(runtime.storage.AssetType.Sound, md5).then(soundAsset => { sound.data = soundAsset.data; return runtime.audioEngine.decodeSound(sound).then(() => sound); }); diff --git a/src/playground/playground.js b/src/playground/playground.js index dbef3606..fe06b414 100644 --- a/src/playground/playground.js +++ b/src/playground/playground.js @@ -47,7 +47,7 @@ window.onload = function () { Scratch.vm = vm; const storage = new Scratch.Storage(); - const AssetType = Scratch.Storage.AssetType; + const AssetType = storage.AssetType; storage.addWebSource([AssetType.Project], getProjectUrl); storage.addWebSource([AssetType.ImageVector, AssetType.ImageBitmap, AssetType.Sound], getAssetUrl); vm.attachStorage(storage); diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 8dfed97a..8e4e9bcf 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -2,7 +2,6 @@ const EventEmitter = require('events'); const log = require('./util/log'); const Runtime = require('./engine/runtime'); -const ScratchStorage = require('scratch-storage'); const sb2import = require('./import/sb2import'); const StringUtil = require('./util/string-util'); @@ -11,8 +10,6 @@ const loadSound = require('./import/load-sound.js'); const RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; -const AssetType = ScratchStorage.AssetType; - /** * Handles connections between blocks, stage, and extensions. * @constructor @@ -170,12 +167,13 @@ class VirtualMachine extends EventEmitter { * @param {string} id - the ID of the project to download, as a string. */ downloadProjectId (id) { - if (!this.runtime.storage) { + const storage = this.runtime.storage; + if (!storage) { log.error('No storage module present; cannot load project: ', id); return; } const vm = this; - const promise = this.runtime.storage.load(AssetType.Project, id); + const promise = storage.load(storage.AssetType.Project, id); promise.then(projectAsset => { vm.loadProject(projectAsset.decodeText()); }); diff --git a/test/fixtures/attach-test-storage.js b/test/fixtures/attach-test-storage.js index 8e5bdcac..5b3dae08 100644 --- a/test/fixtures/attach-test-storage.js +++ b/test/fixtures/attach-test-storage.js @@ -38,7 +38,7 @@ var getAssetUrl = function (asset) { */ var attachTestStorage = function (vm) { var storage = new ScratchStorage(); - var AssetType = ScratchStorage.AssetType; + var AssetType = storage.AssetType; storage.addWebSource([AssetType.Project], getProjectUrl); storage.addWebSource([AssetType.ImageVector, AssetType.ImageBitmap, AssetType.Sound], getAssetUrl); vm.attachStorage(storage); From 306e62b8b177190892eba62fe78ef69700d1314b Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Fri, 14 Apr 2017 14:07:21 +0000 Subject: [PATCH 302/721] chore(package): update webpack to version 2.4.1 Closes #509 https://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1904e174..354bed6a 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "stats.js": "^0.17.0", "tap": "^10.2.0", "travis-after-all": "^1.4.4", - "webpack": "2.2.1", + "webpack": "^2.4.1", "webpack-dev-server": "^2.4.1" } } From e91865fc2187d5135f91ff66ca3a3a2ff9bf16f0 Mon Sep 17 00:00:00 2001 From: john Date: Fri, 21 Apr 2017 16:36:33 +0800 Subject: [PATCH 303/721] the right quots is mission in blockToXML the right quote is mission in blockToXML, and it will result in the following code execution errors vm.on('workspaceUpdate', function (data) { workspace.clear(); var dom = Blockly.Xml.textToDom(data.xml); Blockly.Xml.domToWorkspace(dom, workspace); }); --- src/engine/blocks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 312c7377..0c90d1a6 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -407,7 +407,7 @@ class Blocks { id="${block.id}" type="${block.opcode}" ${block.topLevel ? - `x="${block.x} y="${block.y}` : + `x="${block.x}" y="${block.y}"` : '' } >`; From 1cb4c331789acf4b4c3e7892f04b8ef7f2ae8737 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Fri, 21 Apr 2017 20:25:30 +0000 Subject: [PATCH 304/721] chore(package): update babel-loader to version 7.0.0 https://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 354bed6a..3e359b39 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "adm-zip": "0.4.7", "babel-core": "^6.24.1", "babel-eslint": "^7.1.1", - "babel-loader": "^6.4.1", + "babel-loader": "^7.0.0", "babel-preset-es2015": "^6.24.1", "copy-webpack-plugin": "4.0.1", "eslint": "^3.16.0", From a158db8aff22138b741e8b5c53b8064f58b54974 Mon Sep 17 00:00:00 2001 From: SillyInventor Date: Mon, 24 Apr 2017 10:04:06 -0400 Subject: [PATCH 305/721] Implemented Proposed Fixes Moved 'fake renderer' to fixtures Fixed spacing after () Removed extra line break --- test/fixtures/fake-renderer.js | 66 ++++++++++++++++++++ test/unit/sprites_rendered-target.js | 92 +++++----------------------- 2 files changed, 83 insertions(+), 75 deletions(-) create mode 100644 test/fixtures/fake-renderer.js diff --git a/test/fixtures/fake-renderer.js b/test/fixtures/fake-renderer.js new file mode 100644 index 00000000..2865b483 --- /dev/null +++ b/test/fixtures/fake-renderer.js @@ -0,0 +1,66 @@ +var FakeRenderer = function () { + this.unused = ''; + this.x = 0; + this.y = 0; + this.order = 0; + this.spriteCount = 5; +}; + +FakeRenderer.prototype.createDrawable = function () { + return true; +}; + +FakeRenderer.prototype.getFencedPositionOfDrawable = function (d, p) { // eslint-disable-line no-unused-vars + return [p[0], p[1]]; +}; + +FakeRenderer.prototype.updateDrawableProperties = function (d, p) { // eslint-disable-line no-unused-vars + if (p.position) { + this.x = p.position[0]; + this.y = p.position[1]; + } + return true; +}; + +FakeRenderer.prototype.getSkinSize = function (d) { // eslint-disable-line no-unused-vars + return [0, 0]; +}; + +FakeRenderer.prototype.pick = function (x, y, a, b, d) { // eslint-disable-line no-unused-vars + return true; +}; + +FakeRenderer.prototype.isTouchingColor = function (d, c) { // eslint-disable-line no-unused-vars + return true; +}; + +FakeRenderer.prototype.setDrawableOrder = function (d, l, optA, optB) { // eslint-disable-line no-unused-vars + return true; +}; + +FakeRenderer.prototype.getBounds = function (d) { // eslint-disable-line no-unused-vars + return {left: this.x, right: this.x, top: this.y, bottom: this.y}; +}; + +FakeRenderer.prototype.setDrawableOrder = function (d, a, optA, optB) { // eslint-disable-line no-unused-vars + if (d === 999) return 1; // fake for test case + if (optA) { + a += this.order; + } + if (optB) { + a = Math.max(a, optB); + } + a = Math.max(a, 0); + this.order = Math.min(a, this.spriteCount); + return this.order; +}; + +FakeRenderer.prototype.pick = function (x, y, a, b, c) { // eslint-disable-line no-unused-vars + return c[0]; +}; + +FakeRenderer.prototype.isTouchingColor = function (a, b) { // eslint-disable-line no-unused-vars + return false; +}; + +module.exports = FakeRenderer; diff --git a/test/unit/sprites_rendered-target.js b/test/unit/sprites_rendered-target.js index 33404028..2fb9fc2a 100644 --- a/test/unit/sprites_rendered-target.js +++ b/test/unit/sprites_rendered-target.js @@ -2,65 +2,7 @@ var test = require('tap').test; var RenderedTarget = require('../../src/sprites/rendered-target'); var Sprite = require('../../src/sprites/sprite'); var Runtime = require('../../src/engine/runtime'); - - -var fakeRenderer = function (){ - var renderer = new Object(); - renderer.unused = ''; - renderer.x = 0; - renderer.y = 0; - renderer.order = 0; - renderer.spriteCount = 5; - renderer.createDrawable = function () { - return true; - }; - renderer.getFencedPositionOfDrawable = function (d, p) { // eslint-disable-line no-unused-vars - return [p[0], p[1]]; - }; - renderer.updateDrawableProperties = function (d, p) { // eslint-disable-line no-unused-vars - if (p.position) { - renderer.x = p.position[0]; - renderer.y = p.position[1]; - } - return true; - }; - renderer.getSkinSize = function (d) { // eslint-disable-line no-unused-vars - return [0, 0]; - }; - renderer.pick = function (x, y, a, b, d) { // eslint-disable-line no-unused-vars - return true; - }; - renderer.isTouchingColor = function (d, c) { // eslint-disable-line no-unused-vars - return true; - }; - renderer.setDrawableOrder = function (d, l, optA, optB) { // eslint-disable-line no-unused-vars - return true; - }; - renderer.getBounds = function (d) { // eslint-disable-line no-unused-vars - return {left: renderer.x, right: renderer.x, top: renderer.y, bottom: renderer.y}; - }; - renderer.setDrawableOrder = function (d, a, optA, optB){ // eslint-disable-line no-unused-vars - if (d === 999) return 1; // fake for test case - if (optA) { - a += renderer.order; - } - if (optB) { - a = Math.max(a, optB); - } - a = Math.max(a, 0); - renderer.order = Math.min(a, renderer.spriteCount); - return renderer.order; - }; - renderer.pick = function (x, y, a, b, c){ // eslint-disable-line no-unused-vars - return c[0]; - }; - renderer.isTouchingColor = function (a, b){ // eslint-disable-line no-unused-vars - return false; - }; - - return renderer; -}; - +var FakeRenderer = require('../fixtures/fake-renderer'); test('clone effects', function (t) { // Create two clones and ensure they have different graphic effect objects. @@ -76,7 +18,7 @@ test('setxy', function (t) { var s = new Sprite(); var r = new Runtime(); var a = new RenderedTarget(s, r); - var renderer = fakeRenderer(); + var renderer = new FakeRenderer(); a.renderer = renderer; a.setXY(123, 321, true); t.equals(a.x, 123); @@ -88,7 +30,7 @@ test('direction', function (t) { var s = new Sprite(); var r = new Runtime(); var a = new RenderedTarget(s, r); - var renderer = fakeRenderer(); + var renderer = new FakeRenderer(); a.renderer = renderer; a.setDirection(123); t.equals(a._getRenderedDirectionAndScale().direction, 123); @@ -99,7 +41,7 @@ test('setSay', function (t) { var s = new Sprite(); var r = new Runtime(); var a = new RenderedTarget(s, r); - var renderer = fakeRenderer(); + var renderer = new FakeRenderer(); a.renderer = renderer; a.setSay(); a.setSay('types not specified', 'message'); @@ -110,7 +52,7 @@ test('setVisible', function (t) { var s = new Sprite(); var r = new Runtime(); var a = new RenderedTarget(s, r); - var renderer = fakeRenderer(); + var renderer = new FakeRenderer(); a.renderer = renderer; a.setVisible(true); t.end(); @@ -120,7 +62,7 @@ test('setSize', function (t) { var s = new Sprite(); var r = new Runtime(); var a = new RenderedTarget(s, r); - var renderer = fakeRenderer(); + var renderer = new FakeRenderer(); a.renderer = renderer; a.setSize(123); t.equals(a._getRenderedDirectionAndScale().scale[0], 123); @@ -131,7 +73,7 @@ test('set and clear effects', function (t) { var s = new Sprite(); var r = new Runtime(); var a = new RenderedTarget(s, r); - var renderer = fakeRenderer(); + var renderer = new FakeRenderer(); a.renderer = renderer; for (var effect in a.effects) { a.setEffect(effect, 1); @@ -150,7 +92,7 @@ test('setCostume', function (t) { var r = new Runtime(); s.costumes = [o]; var a = new RenderedTarget(s, r); - var renderer = fakeRenderer(); + var renderer = new FakeRenderer(); a.renderer = renderer; a.setCostume(0); t.end(); @@ -160,7 +102,7 @@ test('setRotationStyle', function (t) { var s = new Sprite(); var r = new Runtime(); var a = new RenderedTarget(s, r); - var renderer = fakeRenderer(); + var renderer = new FakeRenderer(); a.renderer = renderer; a.setRotationStyle(RenderedTarget.ROTATION_STYLE_NONE); t.end(); @@ -169,7 +111,7 @@ test('setRotationStyle', function (t) { test('getBounds', function (t) { var s = new Sprite(); var r = new Runtime(); - var renderer = fakeRenderer(); + var renderer = new FakeRenderer(); r.attachRenderer(renderer); var a = new RenderedTarget(s, r); a.renderer = renderer; @@ -182,7 +124,7 @@ test('getBounds', function (t) { test('isTouchingPoint', function (t) { var s = new Sprite(); var r = new Runtime(); - var renderer = fakeRenderer(); + var renderer = new FakeRenderer(); r.attachRenderer(renderer); var a = new RenderedTarget(s, r); a.renderer = renderer; @@ -193,7 +135,7 @@ test('isTouchingPoint', function (t) { test('isTouchingEdge', function (t) { var s = new Sprite(); var r = new Runtime(); - var renderer = fakeRenderer(); + var renderer = new FakeRenderer(); r.attachRenderer(renderer); var a = new RenderedTarget(s, r); a.renderer = renderer; @@ -206,7 +148,7 @@ test('isTouchingEdge', function (t) { test('isTouchingSprite', function (t) { var s = new Sprite(); var r = new Runtime(); - var renderer = fakeRenderer(); + var renderer = new FakeRenderer(); r.attachRenderer(renderer); var a = new RenderedTarget(s, r); a.renderer = renderer; @@ -217,7 +159,7 @@ test('isTouchingSprite', function (t) { test('isTouchingColor', function (t) { var s = new Sprite(); var r = new Runtime(); - var renderer = fakeRenderer(); + var renderer = new FakeRenderer(); r.attachRenderer(renderer); var a = new RenderedTarget(s, r); a.renderer = renderer; @@ -228,7 +170,7 @@ test('isTouchingColor', function (t) { test('colorIsTouchingColor', function (t) { var s = new Sprite(); var r = new Runtime(); - var renderer = fakeRenderer(); + var renderer = new FakeRenderer(); r.attachRenderer(renderer); var a = new RenderedTarget(s, r); a.renderer = renderer; @@ -239,7 +181,7 @@ test('colorIsTouchingColor', function (t) { test('layers', function (t) { var s = new Sprite(); var r = new Runtime(); - var renderer = fakeRenderer(); + var renderer = new FakeRenderer(); var o = new Object(); r.attachRenderer(renderer); var a = new RenderedTarget(s, r); @@ -257,7 +199,7 @@ test('layers', function (t) { test('keepInFence', function (t) { var s = new Sprite(); var r = new Runtime(); - var renderer = fakeRenderer(); + var renderer = new FakeRenderer(); r.attachRenderer(renderer); var a = new RenderedTarget(s, r); a.renderer = renderer; From bafdf8d9f240e215d8b79f5ca8405ae9aa40a83e Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 20 Apr 2017 19:17:05 -0400 Subject: [PATCH 306/721] Use ES6 linting rules in the project root Update all tests for `no-var` and `prefer-arrow-callback` (using `--fix`) --- .eslintrc.js | 2 +- test/fixtures/attach-test-storage.js | 22 +-- test/fixtures/extract.js | 4 +- test/fixtures/fake-renderer.js | 2 +- test/integration/complex.js | 40 ++--- test/integration/control.js | 28 +-- test/integration/data.js | 26 +-- test/integration/event.js | 28 +-- test/integration/hat-execution-order.js | 30 ++-- test/integration/import_sb2.js | 26 +-- test/integration/looks.js | 28 +-- test/integration/motion.js | 28 +-- test/integration/pen.js | 26 +-- test/integration/procedure.js | 28 +-- test/integration/sensing.js | 28 +-- test/integration/sound.js | 28 +-- test/unit/blocks_control.js | 62 +++---- test/unit/blocks_operators.js | 78 ++++----- test/unit/engine_adapter.js | 72 ++++---- test/unit/engine_blocks.js | 96 +++++------ test/unit/engine_runtime.js | 8 +- test/unit/engine_sequencer.js | 86 +++++----- test/unit/engine_thread.js | 84 ++++----- test/unit/io_clock.js | 22 +-- test/unit/io_keyboard.js | 36 ++-- test/unit/io_mouse.js | 24 +-- test/unit/spec.js | 8 +- test/unit/sprites_rendered-target.js | 218 ++++++++++++------------ test/unit/util_cast.js | 28 +-- test/unit/util_color.js | 26 +-- test/unit/util_math.js | 14 +- test/unit/util_string.js | 8 +- test/unit/util_timer.js | 26 +-- test/unit/util_xml.js | 10 +- test/unit/virtual-machine.js | 42 ++--- webpack.config.js | 10 +- 36 files changed, 666 insertions(+), 666 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 36ff570d..3010f60d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,3 +1,3 @@ module.exports = { - extends: ['scratch', 'scratch/node'] + extends: ['scratch', 'scratch/node', 'scratch/es6'] }; diff --git a/test/fixtures/attach-test-storage.js b/test/fixtures/attach-test-storage.js index 5b3dae08..3e75f596 100644 --- a/test/fixtures/attach-test-storage.js +++ b/test/fixtures/attach-test-storage.js @@ -1,15 +1,15 @@ -var ScratchStorage = require('scratch-storage'); +const ScratchStorage = require('scratch-storage'); -var ASSET_SERVER = 'https://cdn.assets.scratch.mit.edu/'; -var PROJECT_SERVER = 'https://cdn.projects.scratch.mit.edu/'; +const ASSET_SERVER = 'https://cdn.assets.scratch.mit.edu/'; +const PROJECT_SERVER = 'https://cdn.projects.scratch.mit.edu/'; /** * @param {Asset} asset - calculate a URL for this asset. * @returns {string} a URL to download a project file. */ -var getProjectUrl = function (asset) { - var assetIdParts = asset.assetId.split('.'); - var assetUrlParts = [PROJECT_SERVER, 'internalapi/project/', assetIdParts[0], '/get/']; +const getProjectUrl = function (asset) { + const assetIdParts = asset.assetId.split('.'); + const assetUrlParts = [PROJECT_SERVER, 'internalapi/project/', assetIdParts[0], '/get/']; if (assetIdParts[1]) { assetUrlParts.push(assetIdParts[1]); } @@ -20,8 +20,8 @@ var getProjectUrl = function (asset) { * @param {Asset} asset - calculate a URL for this asset. * @returns {string} a URL to download a project asset (PNG, WAV, etc.) */ -var getAssetUrl = function (asset) { - var assetUrlParts = [ +const getAssetUrl = function (asset) { + const assetUrlParts = [ ASSET_SERVER, 'internalapi/asset/', asset.assetId, @@ -36,9 +36,9 @@ var getAssetUrl = function (asset) { * Construct a new instance of ScratchStorage, provide it with default web sources, and attach it to the provided VM. * @param {VirtualMachine} vm - the VM which will own the new ScratchStorage instance. */ -var attachTestStorage = function (vm) { - var storage = new ScratchStorage(); - var AssetType = storage.AssetType; +const attachTestStorage = function (vm) { + const storage = new ScratchStorage(); + const AssetType = storage.AssetType; storage.addWebSource([AssetType.Project], getProjectUrl); storage.addWebSource([AssetType.ImageVector, AssetType.ImageBitmap, AssetType.Sound], getAssetUrl); vm.attachStorage(storage); diff --git a/test/fixtures/extract.js b/test/fixtures/extract.js index fc33cf9a..09826130 100644 --- a/test/fixtures/extract.js +++ b/test/fixtures/extract.js @@ -1,6 +1,6 @@ -var AdmZip = require('adm-zip'); +const AdmZip = require('adm-zip'); module.exports = function (path) { - var zip = new AdmZip(path); + const zip = new AdmZip(path); return zip.readAsText('project.json', 'utf8'); }; diff --git a/test/fixtures/fake-renderer.js b/test/fixtures/fake-renderer.js index 2865b483..a6cc9fc3 100644 --- a/test/fixtures/fake-renderer.js +++ b/test/fixtures/fake-renderer.js @@ -1,4 +1,4 @@ -var FakeRenderer = function () { +const FakeRenderer = function () { this.unused = ''; this.x = 0; this.y = 0; diff --git a/test/integration/complex.js b/test/integration/complex.js index b8777fe1..39bc0c66 100644 --- a/test/integration/complex.js +++ b/test/integration/complex.js @@ -1,32 +1,32 @@ -var fs = require('fs'); -var path = require('path'); -var test = require('tap').test; -var attachTestStorage = require('../fixtures/attach-test-storage'); -var extract = require('../fixtures/extract'); -var VirtualMachine = require('../../src/index'); +const fs = require('fs'); +const path = require('path'); +const test = require('tap').test; +const attachTestStorage = require('../fixtures/attach-test-storage'); +const extract = require('../fixtures/extract'); +const VirtualMachine = require('../../src/index'); -var projectUri = path.resolve(__dirname, '../fixtures/complex.sb2'); -var project = extract(projectUri); +const projectUri = path.resolve(__dirname, '../fixtures/complex.sb2'); +const project = extract(projectUri); -var spriteUri = path.resolve(__dirname, '../fixtures/sprite.json'); -var sprite = fs.readFileSync(spriteUri, 'utf8'); +const spriteUri = path.resolve(__dirname, '../fixtures/sprite.json'); +const sprite = fs.readFileSync(spriteUri, 'utf8'); -test('complex', function (t) { - var vm = new VirtualMachine(); +test('complex', t => { + const vm = new VirtualMachine(); attachTestStorage(vm); // Evaluate playground data and exit - vm.on('playgroundData', function (e) { - var threads = JSON.parse(e.threads); + vm.on('playgroundData', e => { + const threads = JSON.parse(e.threads); t.ok(threads.length === 0); t.end(); process.nextTick(process.exit); }); // Manipulate each target - vm.on('targetsUpdate', function (data) { - var targets = data.targetList; - for (var i in targets) { + vm.on('targetsUpdate', data => { + const targets = data.targetList; + for (const i in targets) { if (targets[i].isStage === true) continue; if (targets[i].name.match(/test/)) continue; @@ -55,12 +55,12 @@ test('complex', function (t) { }); // Start VM, load project, and run - t.doesNotThrow(function () { + t.doesNotThrow(() => { vm.start(); vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project).then(function () { + vm.loadProject(project).then(() => { vm.greenFlag(); // Post IO data @@ -89,7 +89,7 @@ test('complex', function (t) { ); // After two seconds, get playground data and stop - setTimeout(function () { + setTimeout(() => { vm.getPlaygroundData(); vm.stopAll(); }, 2000); diff --git a/test/integration/control.js b/test/integration/control.js index 0d0dbfae..4b65a016 100644 --- a/test/integration/control.js +++ b/test/integration/control.js @@ -1,37 +1,37 @@ -var path = require('path'); -var test = require('tap').test; -var attachTestStorage = require('../fixtures/attach-test-storage'); -var extract = require('../fixtures/extract'); -var VirtualMachine = require('../../src/index'); +const path = require('path'); +const test = require('tap').test; +const attachTestStorage = require('../fixtures/attach-test-storage'); +const extract = require('../fixtures/extract'); +const VirtualMachine = require('../../src/index'); -var uri = path.resolve(__dirname, '../fixtures/control.sb2'); -var project = extract(uri); +const uri = path.resolve(__dirname, '../fixtures/control.sb2'); +const project = extract(uri); -test('control', function (t) { - var vm = new VirtualMachine(); +test('control', t => { + const vm = new VirtualMachine(); attachTestStorage(vm); // Evaluate playground data and exit - vm.on('playgroundData', function (e) { - var threads = JSON.parse(e.threads); + vm.on('playgroundData', e => { + const threads = JSON.parse(e.threads); t.ok(threads.length > 0); t.end(); process.nextTick(process.exit); }); // Start VM, load project, and run - t.doesNotThrow(function () { + t.doesNotThrow(() => { vm.start(); vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project).then(function () { + vm.loadProject(project).then(() => { vm.greenFlag(); }); }); // After two seconds, get playground data and stop - setTimeout(function () { + setTimeout(() => { vm.getPlaygroundData(); vm.stopAll(); }, 2000); diff --git a/test/integration/data.js b/test/integration/data.js index 52b528c5..ba263fec 100644 --- a/test/integration/data.js +++ b/test/integration/data.js @@ -1,34 +1,34 @@ -var path = require('path'); -var test = require('tap').test; -var attachTestStorage = require('../fixtures/attach-test-storage'); -var extract = require('../fixtures/extract'); -var VirtualMachine = require('../../src/index'); +const path = require('path'); +const test = require('tap').test; +const attachTestStorage = require('../fixtures/attach-test-storage'); +const extract = require('../fixtures/extract'); +const VirtualMachine = require('../../src/index'); -var uri = path.resolve(__dirname, '../fixtures/data.sb2'); -var project = extract(uri); +const uri = path.resolve(__dirname, '../fixtures/data.sb2'); +const project = extract(uri); -test('data', function (t) { - var vm = new VirtualMachine(); +test('data', t => { + const vm = new VirtualMachine(); attachTestStorage(vm); // Evaluate playground data and exit - vm.on('playgroundData', function () { + vm.on('playgroundData', () => { // @todo Additional tests t.end(); process.nextTick(process.exit); }); // Start VM, load project, and run - t.doesNotThrow(function () { + t.doesNotThrow(() => { vm.start(); vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project).then(function () { + vm.loadProject(project).then(() => { vm.greenFlag(); // After two seconds, get playground data and stop - setTimeout(function () { + setTimeout(() => { vm.getPlaygroundData(); vm.stopAll(); }, 2000); diff --git a/test/integration/event.js b/test/integration/event.js index f404b2b5..34e21c63 100644 --- a/test/integration/event.js +++ b/test/integration/event.js @@ -1,35 +1,35 @@ -var path = require('path'); -var test = require('tap').test; -var attachTestStorage = require('../fixtures/attach-test-storage'); -var extract = require('../fixtures/extract'); -var VirtualMachine = require('../../src/index'); +const path = require('path'); +const test = require('tap').test; +const attachTestStorage = require('../fixtures/attach-test-storage'); +const extract = require('../fixtures/extract'); +const VirtualMachine = require('../../src/index'); -var uri = path.resolve(__dirname, '../fixtures/event.sb2'); -var project = extract(uri); +const uri = path.resolve(__dirname, '../fixtures/event.sb2'); +const project = extract(uri); -test('event', function (t) { - var vm = new VirtualMachine(); +test('event', t => { + const vm = new VirtualMachine(); attachTestStorage(vm); // Evaluate playground data and exit - vm.on('playgroundData', function (e) { - var threads = JSON.parse(e.threads); + vm.on('playgroundData', e => { + const threads = JSON.parse(e.threads); t.ok(threads.length > 0); t.end(); process.nextTick(process.exit); }); // Start VM, load project, and run - t.doesNotThrow(function () { + t.doesNotThrow(() => { vm.start(); vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project).then(function () { + vm.loadProject(project).then(() => { vm.greenFlag(); // After two seconds, get playground data and stop - setTimeout(function () { + setTimeout(() => { vm.getPlaygroundData(); vm.stopAll(); }, 2000); diff --git a/test/integration/hat-execution-order.js b/test/integration/hat-execution-order.js index 8a04d942..44efcfa0 100644 --- a/test/integration/hat-execution-order.js +++ b/test/integration/hat-execution-order.js @@ -1,22 +1,22 @@ -var path = require('path'); -var test = require('tap').test; -var attachTestStorage = require('../fixtures/attach-test-storage'); -var extract = require('../fixtures/extract'); -var VirtualMachine = require('../../src/index'); +const path = require('path'); +const test = require('tap').test; +const attachTestStorage = require('../fixtures/attach-test-storage'); +const extract = require('../fixtures/extract'); +const VirtualMachine = require('../../src/index'); -var projectUri = path.resolve(__dirname, '../fixtures/hat-execution-order.sb2'); -var project = extract(projectUri); +const projectUri = path.resolve(__dirname, '../fixtures/hat-execution-order.sb2'); +const project = extract(projectUri); -test('complex', function (t) { - var vm = new VirtualMachine(); +test('complex', t => { + const vm = new VirtualMachine(); attachTestStorage(vm); // Evaluate playground data and exit - vm.on('playgroundData', function (e) { - var threads = JSON.parse(e.threads); + vm.on('playgroundData', e => { + const threads = JSON.parse(e.threads); t.ok(threads.length === 0); - var results = vm.runtime.targets[0].lists.results.contents; + const results = vm.runtime.targets[0].lists.results.contents; t.deepEqual(results, ['3', '2', '1', 'stage']); t.end(); @@ -24,16 +24,16 @@ test('complex', function (t) { }); // Start VM, load project, and run - t.doesNotThrow(function () { + t.doesNotThrow(() => { vm.start(); vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project).then(function () { + vm.loadProject(project).then(() => { vm.greenFlag(); // After two seconds, get playground data and stop - setTimeout(function () { + setTimeout(() => { vm.getPlaygroundData(); vm.stopAll(); }, 2000); diff --git a/test/integration/import_sb2.js b/test/integration/import_sb2.js index 010148cf..547b645e 100644 --- a/test/integration/import_sb2.js +++ b/test/integration/import_sb2.js @@ -1,26 +1,26 @@ -var path = require('path'); -var test = require('tap').test; -var attachTestStorage = require('../fixtures/attach-test-storage'); -var extract = require('../fixtures/extract'); +const path = require('path'); +const test = require('tap').test; +const attachTestStorage = require('../fixtures/attach-test-storage'); +const extract = require('../fixtures/extract'); -var renderedTarget = require('../../src/sprites/rendered-target'); -var runtime = require('../../src/engine/runtime'); -var sb2 = require('../../src/import/sb2import'); +const renderedTarget = require('../../src/sprites/rendered-target'); +const runtime = require('../../src/engine/runtime'); +const sb2 = require('../../src/import/sb2import'); -test('spec', function (t) { +test('spec', t => { t.type(sb2, 'function'); t.end(); }); -test('default', function (t) { +test('default', t => { // Get SB2 JSON (string) - var uri = path.resolve(__dirname, '../fixtures/default.sb2'); - var file = extract(uri); + const uri = path.resolve(__dirname, '../fixtures/default.sb2'); + const file = extract(uri); // Create runtime instance & load SB2 into it - var rt = new runtime(); + const rt = new runtime(); attachTestStorage(rt); - sb2(file, rt).then(function (targets) { + sb2(file, rt).then(targets => { // Test t.type(file, 'string'); t.type(rt, 'object'); diff --git a/test/integration/looks.js b/test/integration/looks.js index 5f5313e9..d251d4f8 100644 --- a/test/integration/looks.js +++ b/test/integration/looks.js @@ -1,35 +1,35 @@ -var path = require('path'); -var test = require('tap').test; -var attachTestStorage = require('../fixtures/attach-test-storage'); -var extract = require('../fixtures/extract'); -var VirtualMachine = require('../../src/index'); +const path = require('path'); +const test = require('tap').test; +const attachTestStorage = require('../fixtures/attach-test-storage'); +const extract = require('../fixtures/extract'); +const VirtualMachine = require('../../src/index'); -var uri = path.resolve(__dirname, '../fixtures/looks.sb2'); -var project = extract(uri); +const uri = path.resolve(__dirname, '../fixtures/looks.sb2'); +const project = extract(uri); -test('looks', function (t) { - var vm = new VirtualMachine(); +test('looks', t => { + const vm = new VirtualMachine(); attachTestStorage(vm); // Evaluate playground data and exit - vm.on('playgroundData', function (e) { - var threads = JSON.parse(e.threads); + vm.on('playgroundData', e => { + const threads = JSON.parse(e.threads); t.ok(threads.length === 0); t.end(); process.nextTick(process.exit); }); // Start VM, load project, and run - t.doesNotThrow(function () { + t.doesNotThrow(() => { vm.start(); vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project).then(function () { + vm.loadProject(project).then(() => { vm.greenFlag(); // After two seconds, get playground data and stop - setTimeout(function () { + setTimeout(() => { vm.getPlaygroundData(); vm.stopAll(); }, 2000); diff --git a/test/integration/motion.js b/test/integration/motion.js index cbf0880e..d8b1fc8d 100644 --- a/test/integration/motion.js +++ b/test/integration/motion.js @@ -1,35 +1,35 @@ -var path = require('path'); -var test = require('tap').test; -var attachTestStorage = require('../fixtures/attach-test-storage'); -var extract = require('../fixtures/extract'); -var VirtualMachine = require('../../src/index'); +const path = require('path'); +const test = require('tap').test; +const attachTestStorage = require('../fixtures/attach-test-storage'); +const extract = require('../fixtures/extract'); +const VirtualMachine = require('../../src/index'); -var uri = path.resolve(__dirname, '../fixtures/motion.sb2'); -var project = extract(uri); +const uri = path.resolve(__dirname, '../fixtures/motion.sb2'); +const project = extract(uri); -test('motion', function (t) { - var vm = new VirtualMachine(); +test('motion', t => { + const vm = new VirtualMachine(); attachTestStorage(vm); // Evaluate playground data and exit - vm.on('playgroundData', function (e) { - var threads = JSON.parse(e.threads); + vm.on('playgroundData', e => { + const threads = JSON.parse(e.threads); t.ok(threads.length > 0); t.end(); process.nextTick(process.exit); }); // Start VM, load project, and run - t.doesNotThrow(function () { + t.doesNotThrow(() => { vm.start(); vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project).then(function () { + vm.loadProject(project).then(() => { vm.greenFlag(); // After two seconds, get playground data and stop - setTimeout(function () { + setTimeout(() => { vm.getPlaygroundData(); vm.stopAll(); }, 2000); diff --git a/test/integration/pen.js b/test/integration/pen.js index fde8a52c..f4da55fd 100644 --- a/test/integration/pen.js +++ b/test/integration/pen.js @@ -1,34 +1,34 @@ -var path = require('path'); -var test = require('tap').test; -var attachTestStorage = require('../fixtures/attach-test-storage'); -var extract = require('../fixtures/extract'); -var VirtualMachine = require('../../src/index'); +const path = require('path'); +const test = require('tap').test; +const attachTestStorage = require('../fixtures/attach-test-storage'); +const extract = require('../fixtures/extract'); +const VirtualMachine = require('../../src/index'); -var uri = path.resolve(__dirname, '../fixtures/pen.sb2'); -var project = extract(uri); +const uri = path.resolve(__dirname, '../fixtures/pen.sb2'); +const project = extract(uri); -test('pen', function (t) { - var vm = new VirtualMachine(); +test('pen', t => { + const vm = new VirtualMachine(); attachTestStorage(vm); // Evaluate playground data and exit - vm.on('playgroundData', function () { + vm.on('playgroundData', () => { // @todo Additional tests t.end(); process.nextTick(process.exit); }); // Start VM, load project, and run - t.doesNotThrow(function () { + t.doesNotThrow(() => { vm.start(); vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project).then(function () { + vm.loadProject(project).then(() => { vm.greenFlag(); // After two seconds, get playground data and stop - setTimeout(function () { + setTimeout(() => { vm.getPlaygroundData(); vm.stopAll(); }, 2000); diff --git a/test/integration/procedure.js b/test/integration/procedure.js index 177b2461..4abcf1e4 100644 --- a/test/integration/procedure.js +++ b/test/integration/procedure.js @@ -1,35 +1,35 @@ -var path = require('path'); -var test = require('tap').test; -var attachTestStorage = require('../fixtures/attach-test-storage'); -var extract = require('../fixtures/extract'); -var VirtualMachine = require('../../src/index'); +const path = require('path'); +const test = require('tap').test; +const attachTestStorage = require('../fixtures/attach-test-storage'); +const extract = require('../fixtures/extract'); +const VirtualMachine = require('../../src/index'); -var uri = path.resolve(__dirname, '../fixtures/procedure.sb2'); -var project = extract(uri); +const uri = path.resolve(__dirname, '../fixtures/procedure.sb2'); +const project = extract(uri); -test('procedure', function (t) { - var vm = new VirtualMachine(); +test('procedure', t => { + const vm = new VirtualMachine(); attachTestStorage(vm); // Evaluate playground data and exit - vm.on('playgroundData', function (e) { - var threads = JSON.parse(e.threads); + vm.on('playgroundData', e => { + const threads = JSON.parse(e.threads); t.ok(threads.length === 0); t.end(); process.nextTick(process.exit); }); // Start VM, load project, and run - t.doesNotThrow(function () { + t.doesNotThrow(() => { vm.start(); vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project).then(function () { + vm.loadProject(project).then(() => { vm.greenFlag(); // After two seconds, get playground data and stop - setTimeout(function () { + setTimeout(() => { vm.getPlaygroundData(); vm.stopAll(); }, 2000); diff --git a/test/integration/sensing.js b/test/integration/sensing.js index f90d26d0..ca8fa1bb 100644 --- a/test/integration/sensing.js +++ b/test/integration/sensing.js @@ -1,35 +1,35 @@ -var path = require('path'); -var test = require('tap').test; -var attachTestStorage = require('../fixtures/attach-test-storage'); -var extract = require('../fixtures/extract'); -var VirtualMachine = require('../../src/index'); +const path = require('path'); +const test = require('tap').test; +const attachTestStorage = require('../fixtures/attach-test-storage'); +const extract = require('../fixtures/extract'); +const VirtualMachine = require('../../src/index'); -var uri = path.resolve(__dirname, '../fixtures/sensing.sb2'); -var project = extract(uri); +const uri = path.resolve(__dirname, '../fixtures/sensing.sb2'); +const project = extract(uri); -test('sensing', function (t) { - var vm = new VirtualMachine(); +test('sensing', t => { + const vm = new VirtualMachine(); attachTestStorage(vm); // Evaluate playground data and exit - vm.on('playgroundData', function (e) { - var threads = JSON.parse(e.threads); + vm.on('playgroundData', e => { + const threads = JSON.parse(e.threads); t.ok(threads.length > 0); t.end(); process.nextTick(process.exit); }); // Start VM, load project, and run - t.doesNotThrow(function () { + t.doesNotThrow(() => { vm.start(); vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project).then(function () { + vm.loadProject(project).then(() => { vm.greenFlag(); // After two seconds, get playground data and stop - setTimeout(function () { + setTimeout(() => { vm.getPlaygroundData(); vm.stopAll(); }, 2000); diff --git a/test/integration/sound.js b/test/integration/sound.js index f1f46419..7ab10159 100644 --- a/test/integration/sound.js +++ b/test/integration/sound.js @@ -1,35 +1,35 @@ -var path = require('path'); -var test = require('tap').test; -var attachTestStorage = require('../fixtures/attach-test-storage'); -var extract = require('../fixtures/extract'); -var VirtualMachine = require('../../src/index'); +const path = require('path'); +const test = require('tap').test; +const attachTestStorage = require('../fixtures/attach-test-storage'); +const extract = require('../fixtures/extract'); +const VirtualMachine = require('../../src/index'); -var uri = path.resolve(__dirname, '../fixtures/sound.sb2'); -var project = extract(uri); +const uri = path.resolve(__dirname, '../fixtures/sound.sb2'); +const project = extract(uri); -test('sound', function (t) { - var vm = new VirtualMachine(); +test('sound', t => { + const vm = new VirtualMachine(); attachTestStorage(vm); // Evaluate playground data and exit - vm.on('playgroundData', function (e) { - var threads = JSON.parse(e.threads); + vm.on('playgroundData', e => { + const threads = JSON.parse(e.threads); t.ok(threads.length > 0); t.end(); process.nextTick(process.exit); }); // Start VM, load project, and run - t.doesNotThrow(function () { + t.doesNotThrow(() => { vm.start(); vm.clear(); vm.setCompatibilityMode(false); vm.setTurboMode(false); - vm.loadProject(project).then(function () { + vm.loadProject(project).then(() => { vm.greenFlag(); // After two seconds, get playground data and stop - setTimeout(function () { + setTimeout(() => { vm.getPlaygroundData(); vm.stopAll(); }, 2000); diff --git a/test/unit/blocks_control.js b/test/unit/blocks_control.js index 7867524c..ddb4d9f1 100644 --- a/test/unit/blocks_control.js +++ b/test/unit/blocks_control.js @@ -1,21 +1,21 @@ -var test = require('tap').test; -var Control = require('../../src/blocks/scratch3_control'); -var Runtime = require('../../src/engine/runtime'); +const test = require('tap').test; +const Control = require('../../src/blocks/scratch3_control'); +const Runtime = require('../../src/engine/runtime'); -test('getPrimitives', function (t) { - var rt = new Runtime(); - var c = new Control(rt); +test('getPrimitives', t => { + const rt = new Runtime(); + const c = new Control(rt); t.type(c.getPrimitives(), 'object'); t.end(); }); -test('repeat', function (t) { - var rt = new Runtime(); - var c = new Control(rt); +test('repeat', t => { + const rt = new Runtime(); + const c = new Control(rt); // Test harness (mocks `util`) - var i = 0; - var repeat = 10; + let i = 0; + const repeat = 10; var util = { stackFrame: Object.create(null), startBranch: function () { @@ -31,13 +31,13 @@ test('repeat', function (t) { t.end(); }); -test('repeatUntil', function (t) { - var rt = new Runtime(); - var c = new Control(rt); +test('repeatUntil', t => { + const rt = new Runtime(); + const c = new Control(rt); // Test harness (mocks `util`) - var i = 0; - var repeat = 10; + let i = 0; + const repeat = 10; var util = { stackFrame: Object.create(null), startBranch: function () { @@ -52,13 +52,13 @@ test('repeatUntil', function (t) { t.end(); }); -test('forever', function (t) { - var rt = new Runtime(); - var c = new Control(rt); +test('forever', t => { + const rt = new Runtime(); + const c = new Control(rt); // Test harness (mocks `util`) - var i = 0; - var util = { + let i = 0; + const util = { startBranch: function (branchNum, isLoop) { i++; t.strictEqual(branchNum, 1); @@ -72,13 +72,13 @@ test('forever', function (t) { t.end(); }); -test('if / ifElse', function (t) { - var rt = new Runtime(); - var c = new Control(rt); +test('if / ifElse', t => { + const rt = new Runtime(); + const c = new Control(rt); // Test harness (mocks `util`) - var i = 0; - var util = { + let i = 0; + const util = { startBranch: function (branchNum) { i += branchNum; } @@ -96,17 +96,17 @@ test('if / ifElse', function (t) { t.end(); }); -test('stop', function (t) { - var rt = new Runtime(); - var c = new Control(rt); +test('stop', t => { + const rt = new Runtime(); + const c = new Control(rt); // Test harness (mocks `util`) - var state = { + const state = { stopAll: 0, stopOtherTargetThreads: 0, stopThisScript: 0 }; - var util = { + const util = { stopAll: function () { state.stopAll++; }, diff --git a/test/unit/blocks_operators.js b/test/unit/blocks_operators.js index dc4110d2..2caf71bc 100644 --- a/test/unit/blocks_operators.js +++ b/test/unit/blocks_operators.js @@ -1,129 +1,129 @@ -var test = require('tap').test; -var Operators = require('../../src/blocks/scratch3_operators'); +const test = require('tap').test; +const Operators = require('../../src/blocks/scratch3_operators'); -var blocks = new Operators(null); +const blocks = new Operators(null); -test('getPrimitives', function (t) { +test('getPrimitives', t => { t.type(blocks.getPrimitives(), 'object'); t.end(); }); -test('add', function (t) { +test('add', t => { t.strictEqual(blocks.add({NUM1: '1', NUM2: '1'}), 2); t.strictEqual(blocks.add({NUM1: 'foo', NUM2: 'bar'}), 0); t.end(); }); -test('subtract', function (t) { +test('subtract', t => { t.strictEqual(blocks.subtract({NUM1: '1', NUM2: '1'}), 0); t.strictEqual(blocks.subtract({NUM1: 'foo', NUM2: 'bar'}), 0); t.end(); }); -test('multiply', function (t) { +test('multiply', t => { t.strictEqual(blocks.multiply({NUM1: '2', NUM2: '2'}), 4); t.strictEqual(blocks.multiply({NUM1: 'foo', NUM2: 'bar'}), 0); t.end(); }); -test('divide', function (t) { +test('divide', t => { t.strictEqual(blocks.divide({NUM1: '2', NUM2: '2'}), 1); t.strictEqual(blocks.divide({NUM1: '1', NUM2: '0'}), Infinity); // @todo t.ok(isNaN(blocks.divide({NUM1: 'foo', NUM2: 'bar'}))); // @todo t.end(); }); -test('lt', function (t) { +test('lt', t => { t.strictEqual(blocks.lt({OPERAND1: '1', OPERAND2: '2'}), true); t.strictEqual(blocks.lt({OPERAND1: '2', OPERAND2: '1'}), false); t.strictEqual(blocks.lt({OPERAND1: '1', OPERAND2: '1'}), false); t.end(); }); -test('equals', function (t) { +test('equals', t => { t.strictEqual(blocks.equals({OPERAND1: '1', OPERAND2: '2'}), false); t.strictEqual(blocks.equals({OPERAND1: '2', OPERAND2: '1'}), false); t.strictEqual(blocks.equals({OPERAND1: '1', OPERAND2: '1'}), true); t.end(); }); -test('gt', function (t) { +test('gt', t => { t.strictEqual(blocks.gt({OPERAND1: '1', OPERAND2: '2'}), false); t.strictEqual(blocks.gt({OPERAND1: '2', OPERAND2: '1'}), true); t.strictEqual(blocks.gt({OPERAND1: '1', OPERAND2: '1'}), false); t.end(); }); -test('and', function (t) { +test('and', t => { t.strictEqual(blocks.and({OPERAND1: true, OPERAND2: true}), true); t.strictEqual(blocks.and({OPERAND1: true, OPERAND2: false}), false); t.strictEqual(blocks.and({OPERAND1: false, OPERAND2: false}), false); t.end(); }); -test('or', function (t) { +test('or', t => { t.strictEqual(blocks.or({OPERAND1: true, OPERAND2: true}), true); t.strictEqual(blocks.or({OPERAND1: true, OPERAND2: false}), true); t.strictEqual(blocks.or({OPERAND1: false, OPERAND2: false}), false); t.end(); }); -test('not', function (t) { +test('not', t => { t.strictEqual(blocks.not({OPERAND: true}), false); t.strictEqual(blocks.not({OPERAND: false}), true); t.end(); }); -test('random', function (t) { - var min = 0; - var max = 100; - var result = blocks.random({FROM: min, TO: max}); +test('random', t => { + const min = 0; + const max = 100; + const result = blocks.random({FROM: min, TO: max}); t.ok(result >= min); t.ok(result <= max); t.end(); }); -test('random - equal', function (t) { - var min = 1; - var max = 1; +test('random - equal', t => { + const min = 1; + const max = 1; t.strictEqual(blocks.random({FROM: min, TO: max}), min); t.end(); }); -test('random - decimal', function (t) { - var min = 0.1; - var max = 10; - var result = blocks.random({FROM: min, TO: max}); +test('random - decimal', t => { + const min = 0.1; + const max = 10; + const result = blocks.random({FROM: min, TO: max}); t.ok(result >= min); t.ok(result <= max); t.end(); }); -test('random - int', function (t) { - var min = 0; - var max = 10; - var result = blocks.random({FROM: min, TO: max}); +test('random - int', t => { + const min = 0; + const max = 10; + const result = blocks.random({FROM: min, TO: max}); t.ok(result >= min); t.ok(result <= max); t.end(); }); -test('random - reverse', function (t) { - var min = 0; - var max = 10; - var result = blocks.random({FROM: max, TO: min}); +test('random - reverse', t => { + const min = 0; + const max = 10; + const result = blocks.random({FROM: max, TO: min}); t.ok(result >= min); t.ok(result <= max); t.end(); }); -test('join', function (t) { +test('join', t => { t.strictEqual(blocks.join({STRING1: 'foo', STRING2: 'bar'}), 'foobar'); t.strictEqual(blocks.join({STRING1: '1', STRING2: '2'}), '12'); t.end(); }); -test('letterOf', function (t) { +test('letterOf', t => { t.strictEqual(blocks.letterOf({STRING: 'foo', LETTER: 0}), ''); t.strictEqual(blocks.letterOf({STRING: 'foo', LETTER: 1}), 'f'); t.strictEqual(blocks.letterOf({STRING: 'foo', LETTER: 2}), 'o'); @@ -133,7 +133,7 @@ test('letterOf', function (t) { t.end(); }); -test('length', function (t) { +test('length', t => { t.strictEqual(blocks.length({STRING: ''}), 0); t.strictEqual(blocks.length({STRING: 'foo'}), 3); t.strictEqual(blocks.length({STRING: '1'}), 1); @@ -141,21 +141,21 @@ test('length', function (t) { t.end(); }); -test('mod', function (t) { +test('mod', t => { t.strictEqual(blocks.mod({NUM1: 1, NUM2: 1}), 0); t.strictEqual(blocks.mod({NUM1: 3, NUM2: 6}), 3); t.strictEqual(blocks.mod({NUM1: -3, NUM2: 6}), 3); t.end(); }); -test('round', function (t) { +test('round', t => { t.strictEqual(blocks.round({NUM: 1}), 1); t.strictEqual(blocks.round({NUM: 1.1}), 1); t.strictEqual(blocks.round({NUM: 1.5}), 2); t.end(); }); -test('mathop', function (t) { +test('mathop', t => { t.strictEqual(blocks.mathop({OPERATOR: 'abs', NUM: -1}), 1); t.strictEqual(blocks.mathop({OPERATOR: 'floor', NUM: 1.5}), 1); t.strictEqual(blocks.mathop({OPERATOR: 'ceiling', NUM: 0.1}), 1); diff --git a/test/unit/engine_adapter.js b/test/unit/engine_adapter.js index 7c6cd2da..37ad1ea3 100644 --- a/test/unit/engine_adapter.js +++ b/test/unit/engine_adapter.js @@ -1,22 +1,22 @@ -var test = require('tap').test; -var adapter = require('../../src/engine/adapter'); -var events = require('../fixtures/events.json'); +const test = require('tap').test; +const adapter = require('../../src/engine/adapter'); +const events = require('../fixtures/events.json'); -test('spec', function (t) { +test('spec', t => { t.type(adapter, 'function'); t.end(); }); -test('invalid inputs', function (t) { - var nothing = adapter('not an object'); +test('invalid inputs', t => { + let nothing = adapter('not an object'); t.type(nothing, 'undefined'); nothing = adapter({noxmlproperty: true}); t.type(nothing, 'undefined'); t.end(); }); -test('create event', function (t) { - var result = adapter(events.create); +test('create event', t => { + const result = adapter(events.create); t.ok(Array.isArray(result)); t.equal(result.length, 2); @@ -43,8 +43,8 @@ test('create event', function (t) { t.end(); }); -test('create with branch', function (t) { - var result = adapter(events.createbranch); +test('create with branch', t => { + const result = adapter(events.createbranch); // Outer block t.type(result[0].id, 'string'); t.type(result[0].opcode, 'string'); @@ -54,13 +54,13 @@ test('create with branch', function (t) { t.type(result[0].topLevel, 'boolean'); t.equal(result[0].topLevel, true); // In branch - var branchBlockId = result[0].inputs.SUBSTACK.block; - var branchShadowId = result[0].inputs.SUBSTACK.shadow; + const branchBlockId = result[0].inputs.SUBSTACK.block; + const branchShadowId = result[0].inputs.SUBSTACK.shadow; t.type(branchBlockId, 'string'); t.equal(branchShadowId, null); // Find actual branch block - var branchBlock = null; - for (var i = 0; i < result.length; i++) { + let branchBlock = null; + for (let i = 0; i < result.length; i++) { if (result[i].id === branchBlockId) { branchBlock = result[i]; } @@ -69,8 +69,8 @@ test('create with branch', function (t) { t.end(); }); -test('create with two branches', function (t) { - var result = adapter(events.createtwobranches); +test('create with two branches', t => { + const result = adapter(events.createtwobranches); // Outer block t.type(result[0].id, 'string'); t.type(result[0].opcode, 'string'); @@ -81,18 +81,18 @@ test('create with two branches', function (t) { t.type(result[0].topLevel, 'boolean'); t.equal(result[0].topLevel, true); // In branchs - var firstBranchBlockId = result[0].inputs.SUBSTACK.block; - var secondBranchBlockId = result[0].inputs.SUBSTACK2.block; + const firstBranchBlockId = result[0].inputs.SUBSTACK.block; + const secondBranchBlockId = result[0].inputs.SUBSTACK2.block; t.type(firstBranchBlockId, 'string'); t.type(secondBranchBlockId, 'string'); - var firstBranchShadowBlockId = result[0].inputs.SUBSTACK.shadow; - var secondBranchShadowBlockId = result[0].inputs.SUBSTACK2.shadow; + const firstBranchShadowBlockId = result[0].inputs.SUBSTACK.shadow; + const secondBranchShadowBlockId = result[0].inputs.SUBSTACK2.shadow; t.equal(firstBranchShadowBlockId, null); t.equal(secondBranchShadowBlockId, null); // Find actual branch blocks - var firstBranchBlock = null; - var secondBranchBlock = null; - for (var i = 0; i < result.length; i++) { + let firstBranchBlock = null; + let secondBranchBlock = null; + for (let i = 0; i < result.length; i++) { if (result[i].id === firstBranchBlockId) { firstBranchBlock = result[i]; } @@ -105,8 +105,8 @@ test('create with two branches', function (t) { t.end(); }); -test('create with top-level shadow', function (t) { - var result = adapter(events.createtoplevelshadow); +test('create with top-level shadow', t => { + const result = adapter(events.createtoplevelshadow); t.ok(Array.isArray(result)); t.equal(result.length, 1); @@ -120,8 +120,8 @@ test('create with top-level shadow', function (t) { t.end(); }); -test('create with next connection', function (t) { - var result = adapter(events.createwithnext); +test('create with next connection', t => { + const result = adapter(events.createwithnext); t.ok(Array.isArray(result)); t.equal(result.length, 2); @@ -148,21 +148,21 @@ test('create with next connection', function (t) { t.end(); }); -test('create with obscured shadow', function (t) { - var result = adapter(events.createobscuredshadow); +test('create with obscured shadow', t => { + const result = adapter(events.createobscuredshadow); t.ok(Array.isArray(result)); t.equal(result.length, 4); t.end(); }); -test('create with invalid block xml', function (t) { +test('create with invalid block xml', t => { // Entirely invalid block XML - var result = adapter(events.createinvalid); + const result = adapter(events.createinvalid); t.ok(Array.isArray(result)); t.equal(result.length, 0); // Invalid grandchild tag - var result2 = adapter(events.createinvalidgrandchild); + const result2 = adapter(events.createinvalidgrandchild); t.ok(Array.isArray(result2)); t.equal(result2.length, 1); t.type(result2[0].id, 'string'); @@ -172,15 +172,15 @@ test('create with invalid block xml', function (t) { t.end(); }); -test('create with invalid xml', function (t) { - var result = adapter(events.createbadxml); +test('create with invalid xml', t => { + const result = adapter(events.createbadxml); t.ok(Array.isArray(result)); t.equal(result.length, 0); t.end(); }); -test('create with empty field', function (t) { - var result = adapter(events.createemptyfield); +test('create with empty field', t => { + const result = adapter(events.createemptyfield); t.ok(Array.isArray(result)); t.equal(result.length, 3); t.end(); diff --git a/test/unit/engine_blocks.js b/test/unit/engine_blocks.js index cfb3cdd9..1055cda6 100644 --- a/test/unit/engine_blocks.js +++ b/test/unit/engine_blocks.js @@ -1,8 +1,8 @@ -var test = require('tap').test; -var Blocks = require('../../src/engine/blocks'); +const test = require('tap').test; +const Blocks = require('../../src/engine/blocks'); -test('spec', function (t) { - var b = new Blocks(); +test('spec', t => { + const b = new Blocks(); t.type(Blocks, 'function'); t.type(b, 'object'); @@ -27,8 +27,8 @@ test('spec', function (t) { }); // Getter tests -test('getBlock', function (t) { - var b = new Blocks(); +test('getBlock', t => { + const b = new Blocks(); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -37,16 +37,16 @@ test('getBlock', function (t) { inputs: {}, topLevel: true }); - var block = b.getBlock('foo'); + const block = b.getBlock('foo'); t.type(block, 'object'); - var notBlock = b.getBlock('?'); + const notBlock = b.getBlock('?'); t.type(notBlock, 'undefined'); t.end(); }); -test('getScripts', function (t) { - var b = new Blocks(); - var scripts = b.getScripts(); +test('getScripts', t => { + const b = new Blocks(); + let scripts = b.getScripts(); t.type(scripts, 'object'); t.equals(scripts.length, 0); // Create two top-level blocks and one not. @@ -85,8 +85,8 @@ test('getScripts', function (t) { }); -test('getNextBlock', function (t) { - var b = new Blocks(); +test('getNextBlock', t => { + const b = new Blocks(); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -96,7 +96,7 @@ test('getNextBlock', function (t) { topLevel: true }); - var next = b.getNextBlock('foo'); + let next = b.getNextBlock('foo'); t.equals(next, null); // Add a block with "foo" as its next. @@ -113,14 +113,14 @@ test('getNextBlock', function (t) { t.equals(next, 'foo'); // Block that doesn't exist. - var noBlock = b.getNextBlock('?'); + const noBlock = b.getNextBlock('?'); t.equals(noBlock, null); t.end(); }); -test('getBranch', function (t) { - var b = new Blocks(); +test('getBranch', t => { + const b = new Blocks(); // Single branch b.createBlock({ id: 'foo', @@ -145,17 +145,17 @@ test('getBranch', function (t) { topLevel: false }); - var branch = b.getBranch('foo'); + const branch = b.getBranch('foo'); t.equals(branch, 'foo2'); - var notBranch = b.getBranch('?'); + const notBranch = b.getBranch('?'); t.equals(notBranch, null); t.end(); }); -test('getBranch2', function (t) { - var b = new Blocks(); +test('getBranch2', t => { + const b = new Blocks(); // Second branch b.createBlock({ id: 'foo', @@ -193,16 +193,16 @@ test('getBranch2', function (t) { topLevel: false }); - var branch1 = b.getBranch('foo', 1); - var branch2 = b.getBranch('foo', 2); + const branch1 = b.getBranch('foo', 1); + const branch2 = b.getBranch('foo', 2); t.equals(branch1, 'foo2'); t.equals(branch2, 'foo3'); t.end(); }); -test('getBranch with none', function (t) { - var b = new Blocks(); +test('getBranch with none', t => { + const b = new Blocks(); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -211,14 +211,14 @@ test('getBranch with none', function (t) { inputs: {}, topLevel: true }); - var noBranch = b.getBranch('foo'); + const noBranch = b.getBranch('foo'); t.equals(noBranch, null); t.end(); }); -test('getOpcode', function (t) { - var b = new Blocks(); - var block = { +test('getOpcode', t => { + const b = new Blocks(); + const block = { id: 'foo', opcode: 'TEST_BLOCK', next: null, @@ -227,17 +227,17 @@ test('getOpcode', function (t) { topLevel: true }; b.createBlock(block); - var opcode = b.getOpcode(block); + const opcode = b.getOpcode(block); t.equals(opcode, 'TEST_BLOCK'); - var undefinedBlock = b.getBlock('?'); - var undefinedOpcode = b.getOpcode(undefinedBlock); + const undefinedBlock = b.getBlock('?'); + const undefinedOpcode = b.getOpcode(undefinedBlock); t.equals(undefinedOpcode, null); t.end(); }); // Block events tests -test('create', function (t) { - var b = new Blocks(); +test('create', t => { + const b = new Blocks(); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -253,8 +253,8 @@ test('create', function (t) { t.end(); }); -test('move', function (t) { - var b = new Blocks(); +test('move', t => { + const b = new Blocks(); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -293,8 +293,8 @@ test('move', function (t) { t.end(); }); -test('move into empty', function (t) { - var b = new Blocks(); +test('move into empty', t => { + const b = new Blocks(); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -320,8 +320,8 @@ test('move into empty', function (t) { t.end(); }); -test('move no obscure shadow', function (t) { - var b = new Blocks(); +test('move no obscure shadow', t => { + const b = new Blocks(); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -354,8 +354,8 @@ test('move no obscure shadow', function (t) { t.end(); }); -test('change', function (t) { - var b = new Blocks(); +test('change', t => { + const b = new Blocks(); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -411,8 +411,8 @@ test('change', function (t) { t.end(); }); -test('delete', function (t) { - var b = new Blocks(); +test('delete', t => { + const b = new Blocks(); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -430,10 +430,10 @@ test('delete', function (t) { t.end(); }); -test('delete chain', function (t) { +test('delete chain', t => { // Create a chain of connected blocks and delete the top one. // All of them should be deleted. - var b = new Blocks(); + const b = new Blocks(); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -470,10 +470,10 @@ test('delete chain', function (t) { t.end(); }); -test('delete inputs', function (t) { +test('delete inputs', t => { // Create a block with two inputs, one of which has its own input. // Delete the block - all of them should be deleted. - var b = new Blocks(); + const b = new Blocks(); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', diff --git a/test/unit/engine_runtime.js b/test/unit/engine_runtime.js index 40757c05..be431186 100644 --- a/test/unit/engine_runtime.js +++ b/test/unit/engine_runtime.js @@ -1,8 +1,8 @@ -var test = require('tap').test; -var Runtime = require('../../src/engine/runtime'); +const test = require('tap').test; +const Runtime = require('../../src/engine/runtime'); -test('spec', function (t) { - var r = new Runtime(); +test('spec', t => { + const r = new Runtime(); t.type(Runtime, 'function'); t.type(r, 'object'); diff --git a/test/unit/engine_sequencer.js b/test/unit/engine_sequencer.js index 1e000ad3..e5dcd0be 100644 --- a/test/unit/engine_sequencer.js +++ b/test/unit/engine_sequencer.js @@ -1,15 +1,15 @@ -var test = require('tap').test; -var Sequencer = require('../../src/engine/sequencer'); -var Runtime = require('../../src/engine/runtime'); -var Thread = require('../../src/engine/thread'); -var RenderedTarget = require('../../src/sprites/rendered-target'); -var Sprite = require('../../src/sprites/sprite'); +const test = require('tap').test; +const Sequencer = require('../../src/engine/sequencer'); +const Runtime = require('../../src/engine/runtime'); +const Thread = require('../../src/engine/thread'); +const RenderedTarget = require('../../src/sprites/rendered-target'); +const Sprite = require('../../src/sprites/sprite'); -test('spec', function (t) { +test('spec', t => { t.type(Sequencer, 'function'); - var r = new Runtime(); - var s = new Sequencer(r); + const r = new Runtime(); + const s = new Sequencer(r); t.type(s, 'object'); t.ok(s instanceof Sequencer); @@ -23,13 +23,13 @@ test('spec', function (t) { t.end(); }); -var randomString = function () { - var top = Math.random().toString(36); +const randomString = function () { + const top = Math.random().toString(36); return top.substring(7); }; -var generateBlock = function (id) { - var block = {fields: Object, +const generateBlock = function (id) { + const block = {fields: Object, id: id, inputs: {}, STEPS: Object, @@ -47,8 +47,8 @@ var generateBlock = function (id) { return block; }; -var generateBlockInput = function (id, next, inp) { - var block = {fields: Object, +const generateBlockInput = function (id, next, inp) { + const block = {fields: Object, id: id, inputs: {SUBSTACK: {block: inp, name: 'SUBSTACK'}}, STEPS: Object, @@ -66,20 +66,20 @@ var generateBlockInput = function (id, next, inp) { return block; }; -var generateThread = function (runtime) { - var s = new Sprite(); - var rt = new RenderedTarget(s, runtime); - var th = new Thread(randomString()); +const generateThread = function (runtime) { + const s = new Sprite(); + const rt = new RenderedTarget(s, runtime); + const th = new Thread(randomString()); - var next = randomString(); - var inp = randomString(); - var name = th.topBlock; + let next = randomString(); + let inp = randomString(); + let name = th.topBlock; rt.blocks.createBlock(generateBlockInput(name, next, inp)); th.pushStack(name); rt.blocks.createBlock(generateBlock(inp)); - for (var i = 0; i < 10; i++) { + for (let i = 0; i < 10; i++) { name = next; next = randomString(); inp = randomString(); @@ -97,10 +97,10 @@ var generateThread = function (runtime) { return th; }; -test('stepThread', function (t) { - var r = new Runtime(); - var s = new Sequencer(r); - var th = generateThread(r); +test('stepThread', t => { + const r = new Runtime(); + const s = new Sequencer(r); + let th = generateThread(r); t.notEquals(th.status, Thread.STATUS_DONE); s.stepThread(th); t.strictEquals(th.status, Thread.STATUS_DONE); @@ -115,10 +115,10 @@ test('stepThread', function (t) { t.end(); }); -test('stepToBranch', function (t) { - var r = new Runtime(); - var s = new Sequencer(r); - var th = generateThread(r); +test('stepToBranch', t => { + const r = new Runtime(); + const s = new Sequencer(r); + const th = generateThread(r); s.stepToBranch(th, 2, false); t.strictEquals(th.peekStack(), null); th.popStack(); @@ -132,10 +132,10 @@ test('stepToBranch', function (t) { t.end(); }); -test('retireThread', function (t) { - var r = new Runtime(); - var s = new Sequencer(r); - var th = generateThread(r); +test('retireThread', t => { + const r = new Runtime(); + const s = new Sequencer(r); + const th = generateThread(r); t.strictEquals(th.stack.length, 12); s.retireThread(th); t.strictEquals(th.stack.length, 0); @@ -144,11 +144,11 @@ test('retireThread', function (t) { t.end(); }); -test('stepToProcedure', function (t) { - var r = new Runtime(); - var s = new Sequencer(r); - var th = generateThread(r); - var expectedBlock = th.peekStack(); +test('stepToProcedure', t => { + const r = new Runtime(); + const s = new Sequencer(r); + const th = generateThread(r); + let expectedBlock = th.peekStack(); s.stepToProcedure(th, ''); t.strictEquals(th.peekStack(), expectedBlock); s.stepToProcedure(th, 'faceCode'); @@ -163,10 +163,10 @@ test('stepToProcedure', function (t) { t.end(); }); -test('stepThreads', function (t) { - var r = new Runtime(); +test('stepThreads', t => { + const r = new Runtime(); r.currentStepTime = Infinity; - var s = new Sequencer(r); + const s = new Sequencer(r); t.strictEquals(s.stepThreads().length, 0); generateThread(r); t.strictEquals(r.threads.length, 1); diff --git a/test/unit/engine_thread.js b/test/unit/engine_thread.js index 815de5ca..60419d36 100644 --- a/test/unit/engine_thread.js +++ b/test/unit/engine_thread.js @@ -1,12 +1,12 @@ -var test = require('tap').test; -var Thread = require('../../src/engine/thread'); -var RenderedTarget = require('../../src/sprites/rendered-target'); -var Sprite = require('../../src/sprites/sprite'); +const test = require('tap').test; +const Thread = require('../../src/engine/thread'); +const RenderedTarget = require('../../src/sprites/rendered-target'); +const Sprite = require('../../src/sprites/sprite'); -test('spec', function (t) { +test('spec', t => { t.type(Thread, 'function'); - var th = new Thread('arbitraryString'); + const th = new Thread('arbitraryString'); t.type(th, 'object'); t.ok(th instanceof Thread); t.type(th.pushStack, 'function'); @@ -27,15 +27,15 @@ test('spec', function (t) { t.end(); }); -test('pushStack', function (t) { - var th = new Thread('arbitraryString'); +test('pushStack', t => { + const th = new Thread('arbitraryString'); th.pushStack('arbitraryString'); t.end(); }); -test('popStack', function (t) { - var th = new Thread('arbitraryString'); +test('popStack', t => { + const th = new Thread('arbitraryString'); th.pushStack('arbitraryString'); t.strictEquals(th.popStack(), 'arbitraryString'); t.strictEquals(th.popStack(), undefined); @@ -43,8 +43,8 @@ test('popStack', function (t) { t.end(); }); -test('atStackTop', function (t) { - var th = new Thread('arbitraryString'); +test('atStackTop', t => { + const th = new Thread('arbitraryString'); th.pushStack('arbitraryString'); th.pushStack('secondString'); t.strictEquals(th.atStackTop(), false); @@ -54,8 +54,8 @@ test('atStackTop', function (t) { t.end(); }); -test('reuseStackForNextBlock', function (t) { - var th = new Thread('arbitraryString'); +test('reuseStackForNextBlock', t => { + const th = new Thread('arbitraryString'); th.pushStack('arbitraryString'); th.reuseStackForNextBlock('secondString'); t.strictEquals(th.popStack(), 'secondString'); @@ -63,8 +63,8 @@ test('reuseStackForNextBlock', function (t) { t.end(); }); -test('peekStackFrame', function (t) { - var th = new Thread('arbitraryString'); +test('peekStackFrame', t => { + const th = new Thread('arbitraryString'); th.pushStack('arbitraryString'); t.strictEquals(th.peekStackFrame().warpMode, false); th.popStack(); @@ -73,8 +73,8 @@ test('peekStackFrame', function (t) { t.end(); }); -test('peekParentStackFrame', function (t) { - var th = new Thread('arbitraryString'); +test('peekParentStackFrame', t => { + const th = new Thread('arbitraryString'); th.pushStack('arbitraryString'); th.peekStackFrame().warpMode = true; t.strictEquals(th.peekParentStackFrame(), null); @@ -84,8 +84,8 @@ test('peekParentStackFrame', function (t) { t.end(); }); -test('pushReportedValue', function (t) { - var th = new Thread('arbitraryString'); +test('pushReportedValue', t => { + const th = new Thread('arbitraryString'); th.pushStack('arbitraryString'); th.pushStack('secondString'); th.pushReportedValue('value'); @@ -94,8 +94,8 @@ test('pushReportedValue', function (t) { t.end(); }); -test('peekStack', function (t) { - var th = new Thread('arbitraryString'); +test('peekStack', t => { + const th = new Thread('arbitraryString'); th.pushStack('arbitraryString'); t.strictEquals(th.peekStack(), 'arbitraryString'); th.popStack(); @@ -104,8 +104,8 @@ test('peekStack', function (t) { t.end(); }); -test('PushGetParam', function (t) { - var th = new Thread('arbitraryString'); +test('PushGetParam', t => { + const th = new Thread('arbitraryString'); th.pushStack('arbitraryString'); th.pushParam('testParam', 'testValue'); t.strictEquals(th.peekStackFrame().params.testParam, 'testValue'); @@ -114,11 +114,11 @@ test('PushGetParam', function (t) { t.end(); }); -test('goToNextBlock', function (t) { - var th = new Thread('arbitraryString'); - var s = new Sprite(); - var rt = new RenderedTarget(s, null); - var block1 = {fields: Object, +test('goToNextBlock', t => { + const th = new Thread('arbitraryString'); + const s = new Sprite(); + const rt = new RenderedTarget(s, null); + const block1 = {fields: Object, id: 'arbitraryString', inputs: Object, STEPS: Object, @@ -132,7 +132,7 @@ test('goToNextBlock', function (t) { x: 0, y: 0 }; - var block2 = {fields: Object, + const block2 = {fields: Object, id: 'secondString', inputs: Object, STEPS: Object, @@ -169,11 +169,11 @@ test('goToNextBlock', function (t) { t.end(); }); -test('stopThisScript', function (t) { - var th = new Thread('arbitraryString'); - var s = new Sprite(); - var rt = new RenderedTarget(s, null); - var block1 = {fields: Object, +test('stopThisScript', t => { + const th = new Thread('arbitraryString'); + const s = new Sprite(); + const rt = new RenderedTarget(s, null); + const block1 = {fields: Object, id: 'arbitraryString', inputs: Object, STEPS: Object, @@ -187,7 +187,7 @@ test('stopThisScript', function (t) { x: 0, y: 0 }; - var block2 = {fields: Object, + const block2 = {fields: Object, id: 'secondString', inputs: Object, STEPS: Object, @@ -221,11 +221,11 @@ test('stopThisScript', function (t) { t.end(); }); -test('isRecursiveCall', function (t) { - var th = new Thread('arbitraryString'); - var s = new Sprite(); - var rt = new RenderedTarget(s, null); - var block1 = {fields: Object, +test('isRecursiveCall', t => { + const th = new Thread('arbitraryString'); + const s = new Sprite(); + const rt = new RenderedTarget(s, null); + const block1 = {fields: Object, id: 'arbitraryString', inputs: Object, STEPS: Object, @@ -239,7 +239,7 @@ test('isRecursiveCall', function (t) { x: 0, y: 0 }; - var block2 = {fields: Object, + const block2 = {fields: Object, id: 'secondString', inputs: Object, STEPS: Object, diff --git a/test/unit/io_clock.js b/test/unit/io_clock.js index 12989fcf..1978911e 100644 --- a/test/unit/io_clock.js +++ b/test/unit/io_clock.js @@ -1,10 +1,10 @@ -var test = require('tap').test; -var Clock = require('../../src/io/clock'); -var Runtime = require('../../src/engine/runtime'); +const test = require('tap').test; +const Clock = require('../../src/io/clock'); +const Runtime = require('../../src/engine/runtime'); -test('spec', function (t) { - var rt = new Runtime(); - var c = new Clock(rt); +test('spec', t => { + const rt = new Runtime(); + const c = new Clock(rt); t.type(Clock, 'function'); t.type(c, 'object'); @@ -15,14 +15,14 @@ test('spec', function (t) { t.end(); }); -test('cycle', function (t) { - var rt = new Runtime(); - var c = new Clock(rt); +test('cycle', t => { + const rt = new Runtime(); + const c = new Clock(rt); t.ok(c.projectTimer() <= 0.1); - setTimeout(function () { + setTimeout(() => { c.resetProjectTimer(); - setTimeout(function () { + setTimeout(() => { t.ok(c.projectTimer() > 0); c.pause(); t.ok(c.projectTimer() > 0); diff --git a/test/unit/io_keyboard.js b/test/unit/io_keyboard.js index ad3a65c6..a0a38f74 100644 --- a/test/unit/io_keyboard.js +++ b/test/unit/io_keyboard.js @@ -1,10 +1,10 @@ -var test = require('tap').test; -var Keyboard = require('../../src/io/keyboard'); -var Runtime = require('../../src/engine/runtime'); +const test = require('tap').test; +const Keyboard = require('../../src/io/keyboard'); +const Runtime = require('../../src/engine/runtime'); -test('spec', function (t) { - var rt = new Runtime(); - var k = new Keyboard(rt); +test('spec', t => { + const rt = new Runtime(); + const k = new Keyboard(rt); t.type(k, 'object'); t.type(k.postData, 'function'); @@ -12,9 +12,9 @@ test('spec', function (t) { t.end(); }); -test('space', function (t) { - var rt = new Runtime(); - var k = new Keyboard(rt); +test('space', t => { + const rt = new Runtime(); + const k = new Keyboard(rt); k.postData({ keyCode: 32, @@ -26,9 +26,9 @@ test('space', function (t) { t.end(); }); -test('letter', function (t) { - var rt = new Runtime(); - var k = new Keyboard(rt); +test('letter', t => { + const rt = new Runtime(); + const k = new Keyboard(rt); k.postData({ keyCode: 65, @@ -40,9 +40,9 @@ test('letter', function (t) { t.end(); }); -test('number', function (t) { - var rt = new Runtime(); - var k = new Keyboard(rt); +test('number', t => { + const rt = new Runtime(); + const k = new Keyboard(rt); k.postData({ keyCode: 49, @@ -54,9 +54,9 @@ test('number', function (t) { t.end(); }); -test('keyup', function (t) { - var rt = new Runtime(); - var k = new Keyboard(rt); +test('keyup', t => { + const rt = new Runtime(); + const k = new Keyboard(rt); k.postData({ keyCode: 37, diff --git a/test/unit/io_mouse.js b/test/unit/io_mouse.js index ae4f3fe6..91978666 100644 --- a/test/unit/io_mouse.js +++ b/test/unit/io_mouse.js @@ -1,10 +1,10 @@ -var test = require('tap').test; -var Mouse = require('../../src/io/mouse'); -var Runtime = require('../../src/engine/runtime'); +const test = require('tap').test; +const Mouse = require('../../src/io/mouse'); +const Runtime = require('../../src/engine/runtime'); -test('spec', function (t) { - var rt = new Runtime(); - var m = new Mouse(rt); +test('spec', t => { + const rt = new Runtime(); + const m = new Mouse(rt); t.type(m, 'object'); t.type(m.postData, 'function'); @@ -14,9 +14,9 @@ test('spec', function (t) { t.end(); }); -test('mouseUp', function (t) { - var rt = new Runtime(); - var m = new Mouse(rt); +test('mouseUp', t => { + const rt = new Runtime(); + const m = new Mouse(rt); m.postData({ x: 1, @@ -31,9 +31,9 @@ test('mouseUp', function (t) { t.end(); }); -test('mouseDown', function (t) { - var rt = new Runtime(); - var m = new Mouse(rt); +test('mouseDown', t => { + const rt = new Runtime(); + const m = new Mouse(rt); m.postData({ x: 10, diff --git a/test/unit/spec.js b/test/unit/spec.js index b16f181f..7e211556 100644 --- a/test/unit/spec.js +++ b/test/unit/spec.js @@ -1,8 +1,8 @@ -var test = require('tap').test; -var VirtualMachine = require('../../src/index'); +const test = require('tap').test; +const VirtualMachine = require('../../src/index'); -test('interface', function (t) { - var vm = new VirtualMachine(); +test('interface', t => { + const vm = new VirtualMachine(); t.type(vm, 'object'); t.type(vm.start, 'function'); t.type(vm.greenFlag, 'function'); diff --git a/test/unit/sprites_rendered-target.js b/test/unit/sprites_rendered-target.js index e22f364b..3f8ee2c3 100644 --- a/test/unit/sprites_rendered-target.js +++ b/test/unit/sprites_rendered-target.js @@ -1,24 +1,24 @@ -var test = require('tap').test; -var RenderedTarget = require('../../src/sprites/rendered-target'); -var Sprite = require('../../src/sprites/sprite'); -var Runtime = require('../../src/engine/runtime'); -var FakeRenderer = require('../fixtures/fake-renderer'); +const test = require('tap').test; +const RenderedTarget = require('../../src/sprites/rendered-target'); +const Sprite = require('../../src/sprites/sprite'); +const Runtime = require('../../src/engine/runtime'); +const FakeRenderer = require('../fixtures/fake-renderer'); -test('clone effects', function (t) { +test('clone effects', t => { // Create two clones and ensure they have different graphic effect objects. // Regression test for Github issue #224 - var spr = new Sprite(); - var a = new RenderedTarget(spr, null); - var b = new RenderedTarget(spr, null); + const spr = new Sprite(); + const a = new RenderedTarget(spr, null); + const b = new RenderedTarget(spr, null); t.ok(a.effects !== b.effects); t.end(); }); -test('setxy', function (t) { - var s = new Sprite(); - var r = new Runtime(); - var a = new RenderedTarget(s, r); - var renderer = new FakeRenderer(); +test('setxy', t => { + const s = new Sprite(); + const r = new Runtime(); + const a = new RenderedTarget(s, r); + const renderer = new FakeRenderer(); a.renderer = renderer; a.setXY(123, 321, true); t.equals(a.x, 123); @@ -26,54 +26,54 @@ test('setxy', function (t) { t.end(); }); -test('direction', function (t) { - var s = new Sprite(); - var r = new Runtime(); - var a = new RenderedTarget(s, r); - var renderer = new FakeRenderer(); +test('direction', t => { + const s = new Sprite(); + const r = new Runtime(); + const a = new RenderedTarget(s, r); + const renderer = new FakeRenderer(); a.renderer = renderer; a.setDirection(123); t.equals(a._getRenderedDirectionAndScale().direction, 123); t.end(); }); -test('setSay', function (t) { - var s = new Sprite(); - var r = new Runtime(); - var a = new RenderedTarget(s, r); - var renderer = new FakeRenderer(); +test('setSay', t => { + const s = new Sprite(); + const r = new Runtime(); + const a = new RenderedTarget(s, r); + const renderer = new FakeRenderer(); a.renderer = renderer; a.setSay(); a.setSay('types not specified', 'message'); t.end(); }); -test('setVisible', function (t) { - var s = new Sprite(); - var r = new Runtime(); - var a = new RenderedTarget(s, r); - var renderer = new FakeRenderer(); +test('setVisible', t => { + const s = new Sprite(); + const r = new Runtime(); + const a = new RenderedTarget(s, r); + const renderer = new FakeRenderer(); a.renderer = renderer; a.setVisible(true); t.end(); }); -test('setSize', function (t) { - var s = new Sprite(); - var r = new Runtime(); - var a = new RenderedTarget(s, r); - var renderer = new FakeRenderer(); +test('setSize', t => { + const s = new Sprite(); + const r = new Runtime(); + const a = new RenderedTarget(s, r); + const renderer = new FakeRenderer(); a.renderer = renderer; a.setSize(123); t.equals(a._getRenderedDirectionAndScale().scale[0], 123); t.end(); }); -test('set and clear effects', function (t) { - var s = new Sprite(); - var r = new Runtime(); - var a = new RenderedTarget(s, r); - var renderer = new FakeRenderer(); +test('set and clear effects', t => { + const s = new Sprite(); + const r = new Runtime(); + const a = new RenderedTarget(s, r); + const renderer = new FakeRenderer(); a.renderer = renderer; for (var effect in a.effects) { a.setEffect(effect, 1); @@ -86,34 +86,34 @@ test('set and clear effects', function (t) { t.end(); }); -test('setCostume', function (t) { - var o = new Object(); - var s = new Sprite(); - var r = new Runtime(); +test('setCostume', t => { + const o = new Object(); + const s = new Sprite(); + const r = new Runtime(); s.costumes = [o]; - var a = new RenderedTarget(s, r); - var renderer = new FakeRenderer(); + const a = new RenderedTarget(s, r); + const renderer = new FakeRenderer(); a.renderer = renderer; a.setCostume(0); t.end(); }); -test('setRotationStyle', function (t) { - var s = new Sprite(); - var r = new Runtime(); - var a = new RenderedTarget(s, r); - var renderer = new FakeRenderer(); +test('setRotationStyle', t => { + const s = new Sprite(); + const r = new Runtime(); + const a = new RenderedTarget(s, r); + const renderer = new FakeRenderer(); a.renderer = renderer; a.setRotationStyle(RenderedTarget.ROTATION_STYLE_NONE); t.end(); }); -test('getBounds', function (t) { - var s = new Sprite(); - var r = new Runtime(); - var renderer = new FakeRenderer(); +test('getBounds', t => { + const s = new Sprite(); + const r = new Runtime(); + const renderer = new FakeRenderer(); r.attachRenderer(renderer); - var a = new RenderedTarget(s, r); + const a = new RenderedTarget(s, r); a.renderer = renderer; t.equals(a.getBounds().top, 0); a.setXY(241, 241); @@ -121,23 +121,23 @@ test('getBounds', function (t) { t.end(); }); -test('isTouchingPoint', function (t) { - var s = new Sprite(); - var r = new Runtime(); - var renderer = new FakeRenderer(); +test('isTouchingPoint', t => { + const s = new Sprite(); + const r = new Runtime(); + const renderer = new FakeRenderer(); r.attachRenderer(renderer); - var a = new RenderedTarget(s, r); + const a = new RenderedTarget(s, r); a.renderer = renderer; t.equals(a.isTouchingPoint(), true); t.end(); }); -test('isTouchingEdge', function (t) { - var s = new Sprite(); - var r = new Runtime(); - var renderer = new FakeRenderer(); +test('isTouchingEdge', t => { + const s = new Sprite(); + const r = new Runtime(); + const renderer = new FakeRenderer(); r.attachRenderer(renderer); - var a = new RenderedTarget(s, r); + const a = new RenderedTarget(s, r); a.renderer = renderer; t.equals(a.isTouchingEdge(), false); a.setXY(1000, 1000); @@ -145,46 +145,46 @@ test('isTouchingEdge', function (t) { t.end(); }); -test('isTouchingSprite', function (t) { - var s = new Sprite(); - var r = new Runtime(); - var renderer = new FakeRenderer(); +test('isTouchingSprite', t => { + const s = new Sprite(); + const r = new Runtime(); + const renderer = new FakeRenderer(); r.attachRenderer(renderer); - var a = new RenderedTarget(s, r); + const a = new RenderedTarget(s, r); a.renderer = renderer; t.equals(a.isTouchingSprite('fake'), false); t.end(); }); -test('isTouchingColor', function (t) { - var s = new Sprite(); - var r = new Runtime(); - var renderer = new FakeRenderer(); +test('isTouchingColor', t => { + const s = new Sprite(); + const r = new Runtime(); + const renderer = new FakeRenderer(); r.attachRenderer(renderer); - var a = new RenderedTarget(s, r); + const a = new RenderedTarget(s, r); a.renderer = renderer; t.equals(a.isTouchingColor(), false); t.end(); }); -test('colorIsTouchingColor', function (t) { - var s = new Sprite(); - var r = new Runtime(); - var renderer = new FakeRenderer(); +test('colorIsTouchingColor', t => { + const s = new Sprite(); + const r = new Runtime(); + const renderer = new FakeRenderer(); r.attachRenderer(renderer); - var a = new RenderedTarget(s, r); + const a = new RenderedTarget(s, r); a.renderer = renderer; t.equals(a.colorIsTouchingColor(), false); t.end(); }); -test('layers', function (t) { - var s = new Sprite(); - var r = new Runtime(); - var renderer = new FakeRenderer(); - var o = new Object(); +test('layers', t => { + const s = new Sprite(); + const r = new Runtime(); + const renderer = new FakeRenderer(); + const o = new Object(); r.attachRenderer(renderer); - var a = new RenderedTarget(s, r); + const a = new RenderedTarget(s, r); a.renderer = renderer; a.goToFront(); t.equals(a.renderer.order, 5); @@ -196,12 +196,12 @@ test('layers', function (t) { t.end(); }); -test('keepInFence', function (t) { - var s = new Sprite(); - var r = new Runtime(); - var renderer = new FakeRenderer(); +test('keepInFence', t => { + const s = new Sprite(); + const r = new Runtime(); + const renderer = new FakeRenderer(); r.attachRenderer(renderer); - var a = new RenderedTarget(s, r); + const a = new RenderedTarget(s, r); a.renderer = renderer; t.equals(a.keepInFence(1000, 1000)[0], 240); t.equals(a.keepInFence(-1000, 1000)[0], -240); @@ -210,40 +210,40 @@ test('keepInFence', function (t) { t.end(); }); -test('#stopAll clears graphics effects', function (t) { - var s = new Sprite(); - var r = new Runtime(); - var a = new RenderedTarget(s, r); - var effectName = 'brightness'; +test('#stopAll clears graphics effects', t => { + const s = new Sprite(); + const r = new Runtime(); + const a = new RenderedTarget(s, r); + const effectName = 'brightness'; a.setEffect(effectName, 100); a.onStopAll(); t.equals(a.effects[effectName], 0); t.end(); }); -test('#getCostumes returns the costumes', function (t) { - var spr = new Sprite(); - var a = new RenderedTarget(spr, null); - var costumes = [1, 2, 3]; +test('#getCostumes returns the costumes', t => { + const spr = new Sprite(); + const a = new RenderedTarget(spr, null); + const costumes = [1, 2, 3]; a.sprite.costumes = costumes; t.equals(a.getCostumes(), costumes); t.end(); }); -test('#getSounds returns the sounds', function (t) { - var spr = new Sprite(); - var a = new RenderedTarget(spr, null); - var sounds = [1, 2, 3]; +test('#getSounds returns the sounds', t => { + const spr = new Sprite(); + const a = new RenderedTarget(spr, null); + const sounds = [1, 2, 3]; a.sprite.sounds = sounds; t.equals(a.getSounds(), sounds); t.end(); }); -test('#toJSON returns the sounds and costumes', function (t) { - var spr = new Sprite(); - var a = new RenderedTarget(spr, null); - var sounds = [1, 2, 3]; - var costumes = ['a', 'b', 'c']; +test('#toJSON returns the sounds and costumes', t => { + const spr = new Sprite(); + const a = new RenderedTarget(spr, null); + const sounds = [1, 2, 3]; + const costumes = ['a', 'b', 'c']; a.sprite.sounds = sounds; a.sprite.costumes = costumes; t.same(a.toJSON().sounds, sounds); diff --git a/test/unit/util_cast.js b/test/unit/util_cast.js index 861373bb..8d30df36 100644 --- a/test/unit/util_cast.js +++ b/test/unit/util_cast.js @@ -1,7 +1,7 @@ -var test = require('tap').test; -var cast = require('../../src/util/cast'); +const test = require('tap').test; +const cast = require('../../src/util/cast'); -test('toNumber', function (t) { +test('toNumber', t => { // Numeric t.strictEqual(cast.toNumber(0), 0); t.strictEqual(cast.toNumber(1), 1); @@ -27,7 +27,7 @@ test('toNumber', function (t) { t.end(); }); -test('toBoolean', function (t) { +test('toBoolean', t => { // Numeric t.strictEqual(cast.toBoolean(0), false); t.strictEqual(cast.toBoolean(1), true); @@ -50,7 +50,7 @@ test('toBoolean', function (t) { t.end(); }); -test('toString', function (t) { +test('toString', t => { // Numeric t.strictEqual(cast.toString(0), '0'); t.strictEqual(cast.toString(1), '1'); @@ -73,7 +73,7 @@ test('toString', function (t) { t.end(); }); -test('toRbgColorList', function (t) { +test('toRbgColorList', t => { // Hex (minimal, see "color" util tests) t.deepEqual(cast.toRgbColorList('#000'), [0, 0, 0]); t.deepEqual(cast.toRgbColorList('#000000'), [0, 0, 0]); @@ -91,7 +91,7 @@ test('toRbgColorList', function (t) { t.end(); }); -test('toRbgColorObject', function (t) { +test('toRbgColorObject', t => { // Hex (minimal, see "color" util tests) t.deepEqual(cast.toRgbColorObject('#000'), {r: 0, g: 0, b: 0}); t.deepEqual(cast.toRgbColorObject('#000000'), {r: 0, g: 0, b: 0}); @@ -110,7 +110,7 @@ test('toRbgColorObject', function (t) { t.end(); }); -test('compare', function (t) { +test('compare', t => { // Numeric t.strictEqual(cast.compare(0, 0), 0); t.strictEqual(cast.compare(1, 0), 1); @@ -137,7 +137,7 @@ test('compare', function (t) { t.end(); }); -test('isInt', function (t) { +test('isInt', t => { // Numeric t.strictEqual(cast.isInt(0), true); t.strictEqual(cast.isInt(1), true); @@ -162,9 +162,9 @@ test('isInt', function (t) { t.end(); }); -test('toListIndex', function (t) { - var list = [0, 1, 2, 3, 4, 5]; - var empty = []; +test('toListIndex', t => { + const list = [0, 1, 2, 3, 4, 5]; + const empty = []; // Valid t.strictEqual(cast.toListIndex(1, list.length), 1); @@ -184,13 +184,13 @@ test('toListIndex', function (t) { t.strictEqual(cast.toListIndex('last', empty.length), cast.LIST_INVALID); // "random" - var random = cast.toListIndex('random', list.length); + const random = cast.toListIndex('random', list.length); t.ok(random <= list.length); t.ok(random > 0); t.strictEqual(cast.toListIndex('random', empty.length), cast.LIST_INVALID); // "any" (alias for "random") - var any = cast.toListIndex('any', list.length); + const any = cast.toListIndex('any', list.length); t.ok(any <= list.length); t.ok(any > 0); t.strictEqual(cast.toListIndex('any', empty.length), cast.LIST_INVALID); diff --git a/test/unit/util_color.js b/test/unit/util_color.js index ea68b786..61fc04a6 100644 --- a/test/unit/util_color.js +++ b/test/unit/util_color.js @@ -1,5 +1,5 @@ -var test = require('tap').test; -var color = require('../../src/util/color'); +const test = require('tap').test; +const color = require('../../src/util/color'); /** * Assert that two HSV colors are similar to each other, within a tolerance. @@ -7,7 +7,7 @@ var color = require('../../src/util/color'); * @param {HSVObject} actual - the first HSV color to compare. * @param {HSVObject} expected - the other HSV color to compare. */ -var hsvSimilar = function (t, actual, expected) { +const hsvSimilar = function (t, actual, expected) { if ((Math.abs(actual.h - expected.h) >= 1) || (Math.abs(actual.s - expected.s) >= 0.01) || (Math.abs(actual.v - expected.v) >= 0.01) @@ -25,7 +25,7 @@ var hsvSimilar = function (t, actual, expected) { * @param {RGBObject} actual - the first RGB color to compare. * @param {RGBObject} expected - the other RGB color to compare. */ -var rgbSimilar = function (t, actual, expected) { +const rgbSimilar = function (t, actual, expected) { if ((Math.abs(actual.r - expected.r) >= 1) || (Math.abs(actual.g - expected.g) >= 1) || (Math.abs(actual.b - expected.b) >= 1) @@ -37,7 +37,7 @@ var rgbSimilar = function (t, actual, expected) { } }; -test('decimalToHex', function (t) { +test('decimalToHex', t => { t.strictEqual(color.decimalToHex(0), '#000000'); t.strictEqual(color.decimalToHex(1), '#000001'); t.strictEqual(color.decimalToHex(16777215), '#ffffff'); @@ -46,7 +46,7 @@ test('decimalToHex', function (t) { t.end(); }); -test('decimalToRgb', function (t) { +test('decimalToRgb', t => { t.deepEqual(color.decimalToRgb(0), {a: 255, r: 0, g: 0, b: 0}); t.deepEqual(color.decimalToRgb(1), {a: 255, r: 0, g: 0, b: 1}); t.deepEqual(color.decimalToRgb(16777215), {a: 255, r: 255, g: 255, b: 255}); @@ -55,7 +55,7 @@ test('decimalToRgb', function (t) { t.end(); }); -test('hexToRgb', function (t) { +test('hexToRgb', t => { t.deepEqual(color.hexToRgb('#000'), {r: 0, g: 0, b: 0}); t.deepEqual(color.hexToRgb('#000000'), {r: 0, g: 0, b: 0}); t.deepEqual(color.hexToRgb('#fff'), {r: 255, g: 255, b: 255}); @@ -73,21 +73,21 @@ test('hexToRgb', function (t) { t.end(); }); -test('rgbToHex', function (t) { +test('rgbToHex', t => { t.strictEqual(color.rgbToHex({r: 0, g: 0, b: 0}), '#000000'); t.strictEqual(color.rgbToHex({r: 255, g: 255, b: 255}), '#ffffff'); t.strictEqual(color.rgbToHex({r: 0, g: 255, b: 170}), '#00ffaa'); t.end(); }); -test('rgbToDecimal', function (t) { +test('rgbToDecimal', t => { t.strictEqual(color.rgbToDecimal({r: 0, g: 0, b: 0}), 0); t.strictEqual(color.rgbToDecimal({r: 255, g: 255, b: 255}), 16777215); t.strictEqual(color.rgbToDecimal({r: 0, g: 255, b: 170}), 65450); t.end(); }); -test('hexToDecimal', function (t) { +test('hexToDecimal', t => { t.strictEqual(color.hexToDecimal('#000'), 0); t.strictEqual(color.hexToDecimal('#000000'), 0); t.strictEqual(color.hexToDecimal('#fff'), 16777215); @@ -97,7 +97,7 @@ test('hexToDecimal', function (t) { t.end(); }); -test('hsvToRgb', function (t) { +test('hsvToRgb', t => { rgbSimilar(t, color.hsvToRgb({h: 0, s: 0, v: 0}), {r: 0, g: 0, b: 0}); rgbSimilar(t, color.hsvToRgb({h: 123, s: 0.1234, v: 0}), {r: 0, g: 0, b: 0}); rgbSimilar(t, color.hsvToRgb({h: 0, s: 0, v: 1}), {r: 255, g: 255, b: 255}); @@ -108,7 +108,7 @@ test('hsvToRgb', function (t) { t.end(); }); -test('rgbToHsv', function (t) { +test('rgbToHsv', t => { hsvSimilar(t, color.rgbToHsv({r: 0, g: 0, b: 0}), {h: 0, s: 0, v: 0}); hsvSimilar(t, color.rgbToHsv({r: 64, g: 64, b: 64}), {h: 0, s: 0, v: 0.25}); hsvSimilar(t, color.rgbToHsv({r: 128, g: 128, b: 128}), {h: 0, s: 0, v: 0.5}); @@ -120,7 +120,7 @@ test('rgbToHsv', function (t) { t.end(); }); -test('mixRgb', function (t) { +test('mixRgb', t => { rgbSimilar(t, color.mixRgb({r: 10, g: 20, b: 30}, {r: 30, g: 40, b: 50}, -1), {r: 10, g: 20, b: 30}); rgbSimilar(t, color.mixRgb({r: 10, g: 20, b: 30}, {r: 30, g: 40, b: 50}, 0), {r: 10, g: 20, b: 30}); rgbSimilar(t, color.mixRgb({r: 10, g: 20, b: 30}, {r: 30, g: 40, b: 50}, 0.25), {r: 15, g: 25, b: 35}); diff --git a/test/unit/util_math.js b/test/unit/util_math.js index 4c728dce..d5a1c32f 100644 --- a/test/unit/util_math.js +++ b/test/unit/util_math.js @@ -1,7 +1,7 @@ -var test = require('tap').test; -var math = require('../../src/util/math-util'); +const test = require('tap').test; +const math = require('../../src/util/math-util'); -test('degToRad', function (t) { +test('degToRad', t => { t.strictEqual(math.degToRad(0), 0); t.strictEqual(math.degToRad(1), 0.017453292519943295); t.strictEqual(math.degToRad(180), Math.PI); @@ -10,7 +10,7 @@ test('degToRad', function (t) { t.end(); }); -test('radToDeg', function (t) { +test('radToDeg', t => { t.strictEqual(math.radToDeg(0), 0); t.strictEqual(math.radToDeg(1), 57.29577951308232); t.strictEqual(math.radToDeg(180), 10313.240312354817); @@ -19,7 +19,7 @@ test('radToDeg', function (t) { t.end(); }); -test('clamp', function (t) { +test('clamp', t => { t.strictEqual(math.clamp(0, 0, 10), 0); t.strictEqual(math.clamp(1, 0, 10), 1); t.strictEqual(math.clamp(-10, 0, 10), 0); @@ -27,7 +27,7 @@ test('clamp', function (t) { t.end(); }); -test('wrapClamp', function (t) { +test('wrapClamp', t => { t.strictEqual(math.wrapClamp(0, 0, 10), 0); t.strictEqual(math.wrapClamp(1, 0, 10), 1); t.strictEqual(math.wrapClamp(-10, 0, 10), 1); @@ -35,7 +35,7 @@ test('wrapClamp', function (t) { t.end(); }); -test('tan', function (t) { +test('tan', t => { t.strictEqual(math.tan(90), Infinity); t.strictEqual(math.tan(180), 0); t.strictEqual(math.tan(-90), -Infinity); diff --git a/test/unit/util_string.js b/test/unit/util_string.js index fdcd439d..74133625 100644 --- a/test/unit/util_string.js +++ b/test/unit/util_string.js @@ -1,7 +1,7 @@ -var test = require('tap').test; -var StringUtil = require('../../src/util/string-util'); +const test = require('tap').test; +const StringUtil = require('../../src/util/string-util'); -test('withoutTrailingDigits', function (t) { +test('withoutTrailingDigits', t => { t.strictEqual(StringUtil.withoutTrailingDigits('boeing747'), 'boeing'); t.strictEqual(StringUtil.withoutTrailingDigits('boeing747 '), 'boeing747 '); t.strictEqual(StringUtil.withoutTrailingDigits('boeing𝟨'), 'boeing𝟨'); @@ -10,7 +10,7 @@ test('withoutTrailingDigits', function (t) { t.end(); }); -test('unusedName', function (t) { +test('unusedName', t => { t.strictEqual( StringUtil.unusedName( 'name', diff --git a/test/unit/util_timer.js b/test/unit/util_timer.js index 4780ec44..5c0f2179 100644 --- a/test/unit/util_timer.js +++ b/test/unit/util_timer.js @@ -1,8 +1,8 @@ -var test = require('tap').test; -var Timer = require('../../src/util/timer'); +const test = require('tap').test; +const Timer = require('../../src/util/timer'); -test('spec', function (t) { - var timer = new Timer(); +test('spec', t => { + const timer = new Timer(); t.type(Timer, 'function'); t.type(timer, 'object'); @@ -15,25 +15,25 @@ test('spec', function (t) { t.end(); }); -test('time', function (t) { - var timer = new Timer(); - var time = timer.time(); +test('time', t => { + const timer = new Timer(); + const time = timer.time(); t.ok(Date.now() >= time); t.end(); }); -test('start / timeElapsed', function (t) { - var timer = new Timer(); - var delay = 100; - var threshold = 1000 / 60; // 60 hz +test('start / timeElapsed', t => { + const timer = new Timer(); + const delay = 100; + const threshold = 1000 / 60; // 60 hz // Start timer timer.start(); // Wait and measure timer - setTimeout(function () { - var timeElapsed = timer.timeElapsed(); + setTimeout(() => { + const timeElapsed = timer.timeElapsed(); t.ok(timeElapsed >= 0); t.ok(timeElapsed >= (delay - threshold) && timeElapsed <= (delay + threshold)); diff --git a/test/unit/util_xml.js b/test/unit/util_xml.js index 1906a2ab..d22f37b6 100644 --- a/test/unit/util_xml.js +++ b/test/unit/util_xml.js @@ -1,9 +1,9 @@ -var test = require('tap').test; -var xml = require('../../src/util/xml-escape'); +const test = require('tap').test; +const xml = require('../../src/util/xml-escape'); -test('escape', function (t) { - var input = ''; - var output = '<foo bar="he & llo '"></foo>'; +test('escape', t => { + const input = ''; + const output = '<foo bar="he & llo '"></foo>'; t.strictEqual(xml(input), output); t.end(); }); diff --git a/test/unit/virtual-machine.js b/test/unit/virtual-machine.js index eed537dd..73cefb8a 100644 --- a/test/unit/virtual-machine.js +++ b/test/unit/virtual-machine.js @@ -1,8 +1,8 @@ -var test = require('tap').test; -var VirtualMachine = require('../../src/virtual-machine.js'); +const test = require('tap').test; +const VirtualMachine = require('../../src/virtual-machine.js'); -test('renameSprite throws when there is no sprite with that id', function (t) { - var vm = new VirtualMachine(); +test('renameSprite throws when there is no sprite with that id', t => { + const vm = new VirtualMachine(); vm.runtime.getTargetById = () => null; t.throws( (() => vm.renameSprite('id', 'name')), @@ -11,9 +11,9 @@ test('renameSprite throws when there is no sprite with that id', function (t) { t.end(); }); -test('renameSprite throws when used on a non-sprite target', function (t) { - var vm = new VirtualMachine(); - var fakeTarget = { +test('renameSprite throws when used on a non-sprite target', t => { + const vm = new VirtualMachine(); + const fakeTarget = { isSprite: () => false }; vm.runtime.getTargetById = () => (fakeTarget); @@ -24,9 +24,9 @@ test('renameSprite throws when used on a non-sprite target', function (t) { t.end(); }); -test('renameSprite throws when there is no sprite for given target', function (t) { - var vm = new VirtualMachine(); - var fakeTarget = { +test('renameSprite throws when there is no sprite for given target', t => { + const vm = new VirtualMachine(); + const fakeTarget = { sprite: null, isSprite: () => true }; @@ -38,9 +38,9 @@ test('renameSprite throws when there is no sprite for given target', function (t t.end(); }); -test('renameSprite sets the sprite name', function (t) { - var vm = new VirtualMachine(); - var fakeTarget = { +test('renameSprite sets the sprite name', t => { + const vm = new VirtualMachine(); + const fakeTarget = { sprite: {name: 'original'}, isSprite: () => true }; @@ -50,9 +50,9 @@ test('renameSprite sets the sprite name', function (t) { t.end(); }); -test('renameSprite does not set sprite names to an empty string', function (t) { - var vm = new VirtualMachine(); - var fakeTarget = { +test('renameSprite does not set sprite names to an empty string', t => { + const vm = new VirtualMachine(); + const fakeTarget = { sprite: {name: 'original'}, isSprite: () => true }; @@ -62,9 +62,9 @@ test('renameSprite does not set sprite names to an empty string', function (t) { t.end(); }); -test('renameSprite does not set sprite names to reserved names', function (t) { - var vm = new VirtualMachine(); - var fakeTarget = { +test('renameSprite does not set sprite names to reserved names', t => { + const vm = new VirtualMachine(); + const fakeTarget = { sprite: {name: 'original'}, isSprite: () => true }; @@ -74,8 +74,8 @@ test('renameSprite does not set sprite names to reserved names', function (t) { t.end(); }); -test('renameSprite increments from existing sprite names', function (t) { - var vm = new VirtualMachine(); +test('renameSprite increments from existing sprite names', t => { + const vm = new VirtualMachine(); vm.emitTargetsUpdate = () => {}; vm.runtime.targets = [{ id: 'id1', diff --git a/webpack.config.js b/webpack.config.js index b014e88c..57125680 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,9 +1,9 @@ -var CopyWebpackPlugin = require('copy-webpack-plugin'); -var defaultsDeep = require('lodash.defaultsdeep'); -var path = require('path'); -var webpack = require('webpack'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const defaultsDeep = require('lodash.defaultsdeep'); +const path = require('path'); +const webpack = require('webpack'); -var base = { +const base = { devServer: { contentBase: false, host: '0.0.0.0', From 2a4af8a9ca59affc5b5c7d3d11897bafb10e010f Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 26 Apr 2017 16:50:53 -0400 Subject: [PATCH 307/721] Fix up most linting/merge errors --- src/playground/index.html | 4 +- src/serialization/sb2.js | 4 +- src/serialization/sb3.js | 57 +++++++++++++-------------- src/virtual-machine.js | 68 +++++++++++++++----------------- test/integration/import_sb2.js | 2 +- test/unit/serialization_sb2.js | 72 +++++++++++++++++----------------- test/unit/serialization_sb3.js | 20 +++++----- 7 files changed, 111 insertions(+), 116 deletions(-) diff --git a/src/playground/index.html b/src/playground/index.html index 2d2f1698..d248b984 100644 --- a/src/playground/index.html +++ b/src/playground/index.html @@ -73,7 +73,7 @@ diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index 53a7cfc4..4278656f 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -15,8 +15,8 @@ const specMap = require('./sb2_specmap'); const Variable = require('../engine/variable'); const List = require('../engine/list'); -const loadCostume = require('./load-costume.js'); -const loadSound = require('./load-sound.js'); +const loadCostume = require('../import/load-costume.js'); +const loadSound = require('../import/load-sound.js'); /** * Convert a Scratch 2.0 procedure string (e.g., "my_procedure %s %b %n") diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 7543b4cd..4eb06dd5 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -4,27 +4,26 @@ * JSON and then generates all needed scratch-vm runtime structures. */ -var package = require('../../package.json'); -var Blocks = require('../engine/blocks'); -var RenderedTarget = require('../sprites/rendered-target'); -var Sprite = require('../sprites/sprite'); -var Variable = require('../engine/variable'); -var List = require('../engine/list'); +const vmPackage = require('../../package.json'); +const Blocks = require('../engine/blocks'); +const Sprite = require('../sprites/sprite'); +const Variable = require('../engine/variable'); +const List = require('../engine/list'); /** * Serializes the specified VM runtime. * @param {!Runtime} runtime VM runtime instance to be serialized. * @return {string} Serialized runtime instance. */ -var serialize = function (runtime) { +const serialize = function (runtime) { // Fetch targets - var obj = Object.create(null); + const obj = Object.create(null); obj.targets = runtime.targets; // Assemble metadata - var meta = Object.create(null); + const meta = Object.create(null); meta.semver = '3.0.0'; - meta.vm = package.version; + meta.vm = vmPackage.version; // Attach full user agent string to metadata if available meta.agent = null; @@ -37,36 +36,36 @@ var serialize = function (runtime) { /** * Parse a single "Scratch object" and create all its in-memory VM objects. - * @param {!Object} object From-JSON "Scratch object:" sprite, stage, watcher. + * @param {!object} object From-JSON "Scratch object:" sprite, stage, watcher. * @param {!Runtime} runtime Runtime object to load all structures into. * @return {?Target} Target created (stage or sprite). */ -var parseScratchObject = function (object, runtime) { +const parseScratchObject = function (object, runtime) { if (!object.hasOwnProperty('name')) { // Watcher/monitor - skip this object until those are implemented in VM. // @todo return; } // Blocks container for this object. - var blocks = new Blocks(); + const blocks = new Blocks(); // @todo: For now, load all Scratch objects (stage/sprites) as a Sprite. - var sprite = new Sprite(blocks, runtime); + const sprite = new Sprite(blocks, runtime); // Sprite/stage name from JSON. if (object.hasOwnProperty('name')) { sprite.name = object.name; } if (object.hasOwnProperty('blocks')) { - for (blockId in object.blocks) { + for (let blockId in object.blocks) { blocks.createBlock(object.blocks[blockId]); } - console.log(blocks); + // console.log(blocks); } // Costumes from JSON. if (object.hasOwnProperty('costumes') || object.hasOwnProperty('costume')) { - for (var i = 0; i < object.costumeCount; i++) { - var costume = object.costumes[i]; + for (let i = 0; i < object.costumeCount; i++) { + const costume = object.costumes[i]; // @todo: Make sure all the relevant metadata is being pulled out. sprite.costumes.push({ skin: costume.skin, @@ -79,8 +78,8 @@ var parseScratchObject = function (object, runtime) { } // Sounds from JSON if (object.hasOwnProperty('sounds')) { - for (var s = 0; s < object.sounds.length; s++) { - var sound = object.sounds[s]; + for (let s = 0; s < object.sounds.length; s++) { + const sound = object.sounds[s]; sprite.sounds.push({ format: sound.format, fileUrl: sound.fileUrl, @@ -93,13 +92,13 @@ var parseScratchObject = function (object, runtime) { } } // Create the first clone, and load its run-state from JSON. - var target = sprite.createClone(); + const target = sprite.createClone(); // Add it to the runtime's list of targets. runtime.targets.push(target); // Load target properties from JSON. if (object.hasOwnProperty('variables')) { - for (var j = 0; j < object.variables.length; j++) { - var variable = object.variables[j]; + for (let j = 0; j < object.variables.length; j++) { + const variable = object.variables[j]; target.variables[variable.name] = new Variable( variable.name, variable.value, @@ -108,8 +107,8 @@ var parseScratchObject = function (object, runtime) { } } if (object.hasOwnProperty('lists')) { - for (var k = 0; k < object.lists.length; k++) { - var list = object.lists[k]; + for (let k = 0; k < object.lists.length; k++) { + const list = object.lists[k]; // @todo: monitor properties. target.lists[list.listName] = new List( list.listName, @@ -143,8 +142,8 @@ var parseScratchObject = function (object, runtime) { } target.updateAllDrawableProperties(); - console.log("returning target:"); - console.log(target); + // console.log('returning target:'); + // console.log(target); return target; }; @@ -154,8 +153,8 @@ var parseScratchObject = function (object, runtime) { * @param {string} json Stringified JSON representation of a VM runtime. * @param {Runtime} runtime Runtime instance */ -var deserialize = function (json, runtime) { - for (var i = 0; i < json.targets.length; i++) { +const deserialize = function (json, runtime) { + for (let i = 0; i < json.targets.length; i++) { parseScratchObject(json.targets[i], runtime); } }; diff --git a/src/virtual-machine.js b/src/virtual-machine.js index a69539a0..745d9089 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -145,22 +145,7 @@ class VirtualMachine extends EventEmitter { */ loadProject (json) { // @todo: Handle other formats, e.g., Scratch 1.4, Scratch 3.0. - return this.fromJSON(json, this.runtime).then(targets => { - this.clear(); - for (let n = 0; n < targets.length; n++) { - if (targets[n] !== null) { - this.runtime.targets.push(targets[n]); - targets[n].updateAllDrawableProperties(); - } - } - // Select the first target for editing, e.g., the first sprite. - this.editingTarget = this.runtime.targets[1]; - - // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); - }); + return this.fromJSON(json); } /** @@ -181,7 +166,7 @@ class VirtualMachine extends EventEmitter { } /** - * return a project in a Scratch 3.0 JSON representation. + * @returns {string} Project in a Scratch 3.0 JSON representation. */ saveProjectSb3 () { // @todo: Handle other formats, e.g., Scratch 1.4, Scratch 2.0. @@ -199,6 +184,7 @@ class VirtualMachine extends EventEmitter { /** * Load a project from a Scratch JSON representation. * @param {string} json JSON string representing a project. + * @returns {Promise} Promise that resolves after the project has loaded */ fromJSON (json) { // Clear the current runtime @@ -214,38 +200,46 @@ class VirtualMachine extends EventEmitter { // @todo This is an extremely naïve / dangerous way of determining version. // See `scratch-parser` for a more sophisticated validation // methodology that should be adapted for use here - if ((typeof json.meta !== 'undefined') && (typeof json.meta.semver !== 'undefined') ) { - sb3.deserialize(json, this.runtime); + let deserializer; + if ((typeof json.meta !== 'undefined') && (typeof json.meta.semver !== 'undefined')) { + deserializer = sb3; } else { - sb2.deserialize(json, this.runtime); + deserializer = sb2; } - // Select the first target for editing, e.g., the first sprite. - this.editingTarget = this.runtime.targets[1]; + return deserializer.deserialize(json, this.runtime).then(targets => { + this.clear(); + for (let n = 0; n < targets.length; n++) { + if (targets[n] !== null) { + this.runtime.targets.push(targets[n]); + targets[n].updateAllDrawableProperties(); + } + } + // Select the first target for editing, e.g., the first sprite. + this.editingTarget = this.runtime.targets[1]; - // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); + // Update the VM user's knowledge of targets and blocks on the workspace. + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); + }); } /** * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. * @param {string} json JSON string representing the sprite. + * @returns {Promise} Promise that resolves after the sprite is added */ addSprite2 (json) { // Select new sprite. - this.editingTarget = sb2.deserialize(json, this.runtime, true); - /* @todo Promisify this - .then(targets => { - this.runtime.targets.push(targets[0]); - this.editingTarget = targets[0]; - }) - */ - // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); + return sb2.deserialize(json, this.runtime, true).then(targets => { + this.runtime.targets.push(targets[0]); + this.editingTarget = targets[0]; + // Update the VM user's knowledge of targets and blocks on the workspace. + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); + }); } /** diff --git a/test/integration/import_sb2.js b/test/integration/import_sb2.js index 547b645e..2602783f 100644 --- a/test/integration/import_sb2.js +++ b/test/integration/import_sb2.js @@ -5,7 +5,7 @@ const extract = require('../fixtures/extract'); const renderedTarget = require('../../src/sprites/rendered-target'); const runtime = require('../../src/engine/runtime'); -const sb2 = require('../../src/import/sb2import'); +const sb2 = require('../../src/serialization/sb2'); test('spec', t => { t.type(sb2, 'function'); diff --git a/test/unit/serialization_sb2.js b/test/unit/serialization_sb2.js index 530311d3..7a0bec03 100644 --- a/test/unit/serialization_sb2.js +++ b/test/unit/serialization_sb2.js @@ -1,51 +1,53 @@ -var path = require('path'); -var test = require('tap').test; -var extract = require('../fixtures/extract'); +const path = require('path'); +const test = require('tap').test; +const extract = require('../fixtures/extract'); -var renderedTarget = require('../../src/sprites/rendered-target'); -var runtime = require('../../src/engine/runtime'); -var sb2 = require('../../src/serialization/sb2'); +const RenderedTarget = require('../../src/sprites/rendered-target'); +const Runtime = require('../../src/engine/runtime'); +const sb2 = require('../../src/serialization/sb2'); -test('spec', function (t) { +test('spec', t => { t.type(sb2, 'object'); t.type(sb2.deserialize, 'function'); t.end(); }); -test('default', function (t) { +test('default', t => { // Get SB2 JSON (string) - var uri = path.resolve(__dirname, '../fixtures/default.sb2'); - var file = extract(uri); + const uri = path.resolve(__dirname, '../fixtures/default.sb2'); + const file = extract(uri); // Create runtime instance & load SB2 into it - var rt = new runtime(); - sb2.deserialize(file, rt); + const rt = new Runtime(); + sb2.deserialize(file, rt).then(targets => { + console.dir(targets); - // Test - t.type(file, 'string'); - t.type(rt, 'object'); - t.type(rt.targets, 'object'); + // Test + t.type(file, 'string'); + t.type(rt, 'object'); + t.type(targets, 'object'); - t.ok(rt.targets[0] instanceof renderedTarget); - 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.ok(targets[0] instanceof RenderedTarget); + t.type(targets[0].id, 'string'); + t.type(targets[0].blocks, 'object'); + t.type(targets[0].variables, 'object'); + t.type(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.equal(targets[0].isOriginal, true); + t.equal(targets[0].currentCostume, 0); + t.equal(targets[0].isOriginal, true); + t.equal(targets[0].isStage, true); - t.ok(rt.targets[1] instanceof renderedTarget); - 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.ok(targets[1] instanceof RenderedTarget); + t.type(targets[1].id, 'string'); + t.type(targets[1].blocks, 'object'); + t.type(targets[1].variables, 'object'); + t.type(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(); + t.equal(targets[1].isOriginal, true); + t.equal(targets[1].currentCostume, 0); + t.equal(targets[1].isOriginal, true); + t.equal(targets[1].isStage, false); + t.end(); + }); }); diff --git a/test/unit/serialization_sb3.js b/test/unit/serialization_sb3.js index 0ec56284..761b0b54 100644 --- a/test/unit/serialization_sb3.js +++ b/test/unit/serialization_sb3.js @@ -1,19 +1,19 @@ -var test = require('tap').test; -var VirtualMachine = require('../../src/index'); -var sb3 = require('../../src/serialization/sb3'); +const test = require('tap').test; +const VirtualMachine = require('../../src/index'); +const sb3 = require('../../src/serialization/sb3'); -test('serialize', function (t) { - var vm = new VirtualMachine(); +test('serialize', t => { + const vm = new VirtualMachine(); vm.fromJSON(JSON.stringify(require('../fixtures/demo.json'))); - var result = sb3.serialize(vm.runtime); + const result = sb3.serialize(vm.runtime); console.dir(JSON.stringify(result)); - // @todo Analyize + // @todo Analyze t.end(); }); -test('deserialize', function (t) { - var vm = new VirtualMachine(); - var result = sb3.deserialize('', vm.runtime); +test('deserialize', t => { + const vm = new VirtualMachine(); + const result = sb3.deserialize('', vm.runtime); // @todo Analyize t.end(); }); From b2ea201707e6fc1c4b4780b1959570159cca5979 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 26 Apr 2017 17:45:36 -0400 Subject: [PATCH 308/721] Don't include asset data in exported JSON --- src/sprites/rendered-target.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 9880d475..d48bbd24 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -763,11 +763,23 @@ class RenderedTarget extends Target { this.dragging = false; } + assetListToJSON (assetList) { + const exclude = ['data', 'url']; + return assetList.map(asset => + Object.keys(asset).reduce((rAsset, prop) => { + if (exclude.indexOf(prop) === -1) rAsset[prop] = asset[prop]; + return rAsset; + }, {}) + ); + } + /** * Serialize sprite info, used when emitting events about the sprite * @returns {object} Sprite data as a simple object */ toJSON () { + const costumes = this.assetListToJSON(this.getCostumes()); + const sounds = this.assetListToJSON(this.getSounds()); return { id: this.id, name: this.getName(), @@ -777,15 +789,15 @@ class RenderedTarget extends Target { size: this.size, direction: this.direction, draggable: this.draggable, - costume: this.getCurrentCostume(), - costumeCount: this.getCostumes().length, + costume: costumes[this.currentCostume], + costumeCount: costumes.length, visible: this.visible, rotationStyle: this.rotationStyle, blocks: this.blocks._blocks, variables: this.variables, lists: this.lists, - costumes: this.getCostumes(), - sounds: this.getSounds() + costumes: costumes, + sounds: sounds }; } From c891268672dbabbac28a535afca6b369edc74137 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 27 Apr 2017 11:06:33 -0400 Subject: [PATCH 309/721] Fix sprite not drawing after being added --- src/virtual-machine.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 8e4e9bcf..c0b762bd 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -184,11 +184,12 @@ class VirtualMachine extends EventEmitter { * @param {string} json JSON string representing the sprite. */ addSprite2 (json) { - // Select new sprite. sb2import(json, this.runtime, true).then(targets => { this.runtime.targets.push(targets[0]); this.editingTarget = targets[0]; - // Update the VM user's knowledge of targets and blocks on the workspace. + this.editingTarget.updateAllDrawableProperties(); + + // Update the VM user's knowledge of targets and blocks on the workspace. this.emitTargetsUpdate(); this.emitWorkspaceUpdate(); this.runtime.setEditingTarget(this.editingTarget); From c9ce6776aa9f084dee1fa5bb71da7f6c73f83b47 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 27 Apr 2017 17:08:06 -0400 Subject: [PATCH 310/721] Get SB3 imports working For now, store the md5 + extension on costumes in the VM. This way we can load them the same way as we load SB2 assets. --- src/import/load-costume.js | 1 + src/serialization/sb3.js | 72 +++++++++++++++++----------------- src/sprites/rendered-target.js | 1 + 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/import/load-costume.js b/src/import/load-costume.js index 5d9fdc1d..83ef7800 100644 --- a/src/import/load-costume.js +++ b/src/import/load-costume.js @@ -13,6 +13,7 @@ const log = require('../util/log'); * @returns {?Promise} - a promise which will resolve after skinId is set, or null on error. */ const loadCostume = function (md5ext, costume, runtime) { + costume.md5ext = md5ext; if (!runtime.storage) { log.error('No storage module present; cannot load costume asset: ', md5ext); return Promise.resolve(costume); diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 4eb06dd5..1f46ad94 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -10,6 +10,9 @@ const Sprite = require('../sprites/sprite'); const Variable = require('../engine/variable'); const List = require('../engine/list'); +const loadCostume = require('../import/load-costume.js'); +const loadSound = require('../import/load-sound.js'); + /** * Serializes the specified VM runtime. * @param {!Runtime} runtime VM runtime instance to be serialized. @@ -63,38 +66,33 @@ const parseScratchObject = function (object, runtime) { // console.log(blocks); } // Costumes from JSON. - if (object.hasOwnProperty('costumes') || object.hasOwnProperty('costume')) { - for (let i = 0; i < object.costumeCount; i++) { - const costume = object.costumes[i]; - // @todo: Make sure all the relevant metadata is being pulled out. - sprite.costumes.push({ - skin: costume.skin, - name: costume.name, - bitmapResolution: costume.bitmapResolution, - rotationCenterX: costume.rotationCenterX, - rotationCenterY: costume.rotationCenterY - }); - } - } + const costumePromises = (object.costumes || []).map(costumeSource => { + // @todo: Make sure all the relevant metadata is being pulled out. + const costume = { + skinId: null, + name: costumeSource.name, + bitmapResolution: costumeSource.bitmapResolution, + rotationCenterX: costumeSource.rotationCenterX, + rotationCenterY: costumeSource.rotationCenterY + }; + return loadCostume(costumeSource.md5ext, costume, runtime); + }); // Sounds from JSON - if (object.hasOwnProperty('sounds')) { - for (let s = 0; s < object.sounds.length; s++) { - const sound = object.sounds[s]; - sprite.sounds.push({ - format: sound.format, - fileUrl: sound.fileUrl, - rate: sound.rate, - sampleCount: sound.sampleCount, - soundID: sound.soundID, - name: sound.name, - md5: sound.md5 - }); - } - } + const soundPromises = (object.sounds || []).map(soundSource => { + const sound = { + format: soundSource.format, + fileUrl: soundSource.fileUrl, + rate: soundSource.rate, + sampleCount: soundSource.sampleCount, + soundID: soundSource.soundID, + name: soundSource.name, + md5: soundSource.md5, + data: null + }; + return loadSound(sound, runtime); + }); // Create the first clone, and load its run-state from JSON. const target = sprite.createClone(); - // Add it to the runtime's list of targets. - runtime.targets.push(target); // Load target properties from JSON. if (object.hasOwnProperty('variables')) { for (let j = 0; j < object.variables.length; j++) { @@ -140,11 +138,13 @@ const parseScratchObject = function (object, runtime) { if (object.hasOwnProperty('isStage')) { target.isStage = object.isStage; } - target.updateAllDrawableProperties(); - - // console.log('returning target:'); - // console.log(target); - return target; + Promise.all(costumePromises).then(costumes => { + sprite.costumes = costumes; + }); + Promise.all(soundPromises).then(sounds => { + sprite.sounds = sounds; + }); + return Promise.all(costumePromises.concat(soundPromises)).then(() => target); }; /** @@ -154,9 +154,7 @@ const parseScratchObject = function (object, runtime) { * @param {Runtime} runtime Runtime instance */ const deserialize = function (json, runtime) { - for (let i = 0; i < json.targets.length; i++) { - parseScratchObject(json.targets[i], runtime); - } + return Promise.all(json.targets.map(target => parseScratchObject(target, runtime))); }; module.exports = { diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index d48bbd24..bc863dfe 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -789,6 +789,7 @@ class RenderedTarget extends Target { size: this.size, direction: this.direction, draggable: this.draggable, + currentCostume: this.currentCostume, costume: costumes[this.currentCostume], costumeCount: costumes.length, visible: this.visible, From f785117e1df9ae288fba891f1a1dc1ba84bd4a34 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 27 Apr 2017 17:49:57 -0400 Subject: [PATCH 311/721] Lint and fix failing tests --- src/serialization/sb2.js | 2 +- src/serialization/sb3.js | 7 ++++--- src/sprites/rendered-target.js | 15 +++++++++++---- src/virtual-machine.js | 7 ++++++- test/fixtures/demo.json | 1 + test/integration/import_sb2.js | 6 ++++-- test/unit/serialization_sb2.js | 6 +++--- test/unit/serialization_sb3.js | 13 ++++++++----- 8 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 test/fixtures/demo.json diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index 4278656f..a54b582f 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -276,7 +276,7 @@ const parseScratchObject = function (object, runtime, topLevel) { /** * Top-level handler. Parse provided JSON, * and process the top-level object (the stage object). - * @param {!string} json SB2-format JSON to load. + * @param {!object} json SB2-format JSON to load. * @param {!Runtime} runtime Runtime object to load all structures into. * @param {boolean=} optForceSprite If set, treat as sprite (Sprite2). * @return {?Promise} Promise that resolves to the loaded targets when ready. diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 1f46ad94..b072be19 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -16,7 +16,7 @@ const loadSound = require('../import/load-sound.js'); /** * Serializes the specified VM runtime. * @param {!Runtime} runtime VM runtime instance to be serialized. - * @return {string} Serialized runtime instance. + * @return {object} Serialized runtime instance. */ const serialize = function (runtime) { // Fetch targets @@ -150,11 +150,12 @@ const parseScratchObject = function (object, runtime) { /** * Deserializes the specified representation of a VM runtime and loads it into * the provided runtime instance. - * @param {string} json Stringified JSON representation of a VM runtime. + * @param {object} json JSON representation of a VM runtime. * @param {Runtime} runtime Runtime instance + * @returns {Promise} Promise that resolves to the list of targets after the project is deserialized */ const deserialize = function (json, runtime) { - return Promise.all(json.targets.map(target => parseScratchObject(target, runtime))); + return Promise.all((json.targets || []).map(target => parseScratchObject(target, runtime))); }; module.exports = { diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index bc863dfe..1151d3fd 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -763,14 +763,21 @@ class RenderedTarget extends Target { this.dragging = false; } + + /** + * Helper method to clean out data from each asset when serializing to JSON + * @param {Array} assetList list of costumes or sounds + * @returns {Array} list with raw data removed from each asset + */ assetListToJSON (assetList) { const exclude = ['data', 'url']; - return assetList.map(asset => - Object.keys(asset).reduce((rAsset, prop) => { + return assetList.map(asset => { + if (typeof asset !== 'object') return asset; + return Object.keys(asset).reduce((rAsset, prop) => { if (exclude.indexOf(prop) === -1) rAsset[prop] = asset[prop]; return rAsset; - }, {}) - ); + }, {}); + }); } /** diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 745d9089..87ed8e4d 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -231,7 +231,12 @@ class VirtualMachine extends EventEmitter { * @returns {Promise} Promise that resolves after the sprite is added */ addSprite2 (json) { - // Select new sprite. + // Validate & parse + if (typeof json !== 'string') return; + json = JSON.parse(json); + if (typeof json !== 'object') return; + + // Select new sprite. return sb2.deserialize(json, this.runtime, true).then(targets => { this.runtime.targets.push(targets[0]); this.editingTarget = targets[0]; diff --git a/test/fixtures/demo.json b/test/fixtures/demo.json new file mode 100644 index 00000000..ed8e2cb8 --- /dev/null +++ b/test/fixtures/demo.json @@ -0,0 +1 @@ +{"targets":[{"id":"s_QE@H1K/8hy}:|+gG%x","name":"Stage","isStage":true,"x":0,"y":0,"size":100,"direction":90,"draggable":false,"currentCostume":2,"costume":{"name":"twirl2","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":8,"md5ext":"dbf20d7d4dbcc82d427828bbffde5d8d.png"},"visible":true,"rotationStyle":"all around","blocks":{"7ypP@K@sZk@[e3]h],~|":{"id":"7ypP@K@sZk@[e3]h],~|","opcode":"event_whenflagclicked","inputs":{},"fields":{},"next":"yG|5uo#Ak+sr:az:5:eY","shadow":false,"x":25.5,"y":13.200000000000001,"topLevel":true,"parent":null},"yG|5uo#Ak+sr:az:5:eY":{"id":"yG|5uo#Ak+sr:az:5:eY","opcode":"control_forever","inputs":{"SUBSTACK":{"name":"SUBSTACK","block":"2iU#B(lcgK/gc63NBzN|","shadow":null}},"fields":{},"next":null,"shadow":false,"parent":"7ypP@K@sZk@[e3]h],~|"},"2iU#B(lcgK/gc63NBzN|":{"id":"2iU#B(lcgK/gc63NBzN|","opcode":"control_create_clone_of","inputs":{"CLONE_OPTION":{"name":"CLONE_OPTION","block":"%?Y.y*q)s4{*.qKf8lB:","shadow":"%?Y.y*q)s4{*.qKf8lB:"}},"fields":{},"next":"t}S1*5H.`;8kh{g=}vJy","shadow":false,"parent":"yG|5uo#Ak+sr:az:5:eY"},"%?Y.y*q)s4{*.qKf8lB:":{"id":"%?Y.y*q)s4{*.qKf8lB:","opcode":"control_create_clone_of_menu","inputs":{},"fields":{"CLONE_OPTION":{"name":"CLONE_OPTION","value":"DINO GIF"}},"next":null,"topLevel":false,"parent":"2iU#B(lcgK/gc63NBzN|","shadow":true},"t}S1*5H.`;8kh{g=}vJy":{"id":"t}S1*5H.`;8kh{g=}vJy","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"Uj`3=G9oI~d[C}#2:su[","shadow":"Uj`3=G9oI~d[C}#2:su["}},"fields":{},"next":null,"shadow":false,"parent":"2iU#B(lcgK/gc63NBzN|"},"Uj`3=G9oI~d[C}#2:su[":{"id":"Uj`3=G9oI~d[C}#2:su[","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":0.5}},"next":null,"topLevel":false,"parent":"t}S1*5H.`;8kh{g=}vJy","shadow":true},"S/Jj?9hjDRtY=k:.:c7p":{"id":"S/Jj?9hjDRtY=k:.:c7p","opcode":"event_whenflagclicked","inputs":{},"fields":{},"next":"`d7M;g9pr/DWO`;S#^rr","shadow":false,"x":370.5,"y":11,"topLevel":true,"parent":null},"`d7M;g9pr/DWO`;S#^rr":{"id":"`d7M;g9pr/DWO`;S#^rr","opcode":"control_forever","inputs":{"SUBSTACK":{"name":"SUBSTACK","block":"osMx/MH`NA?V4AkwQc6d","shadow":null}},"fields":{},"next":null,"shadow":false,"parent":"S/Jj?9hjDRtY=k:.:c7p"},"osMx/MH`NA?V4AkwQc6d":{"id":"osMx/MH`NA?V4AkwQc6d","opcode":"looks_changeeffectby","inputs":{"EFFECT":{"name":"EFFECT","block":"Gp@?LHeu1YD1`2)0-Y,L","shadow":"Gp@?LHeu1YD1`2)0-Y,L"},"CHANGE":{"name":"CHANGE","block":":eos6@gH{97`g]POQN@V","shadow":":eos6@gH{97`g]POQN@V"}},"fields":{},"next":"T4gQhzYc.rPpX-^7;^-)","shadow":false,"parent":"`d7M;g9pr/DWO`;S#^rr"},"Gp@?LHeu1YD1`2)0-Y,L":{"id":"Gp@?LHeu1YD1`2)0-Y,L","opcode":"looks_effectmenu","inputs":{},"fields":{"EFFECT":{"name":"EFFECT","value":"color"}},"next":null,"topLevel":false,"parent":"osMx/MH`NA?V4AkwQc6d","shadow":true},":eos6@gH{97`g]POQN@V":{"id":":eos6@gH{97`g]POQN@V","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"osMx/MH`NA?V4AkwQc6d","shadow":true},"T4gQhzYc.rPpX-^7;^-)":{"id":"T4gQhzYc.rPpX-^7;^-)","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"~cS,w!*#5R*K,3yJ9CaJ","shadow":"~cS,w!*#5R*K,3yJ9CaJ"}},"fields":{},"next":null,"shadow":false,"parent":"osMx/MH`NA?V4AkwQc6d"},"~cS,w!*#5R*K,3yJ9CaJ":{"id":"~cS,w!*#5R*K,3yJ9CaJ","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":0.01}},"next":null,"topLevel":false,"parent":"T4gQhzYc.rPpX-^7;^-)","shadow":true},"+M8;.D~|?HC;a~/XN3t;":{"id":"+M8;.D~|?HC;a~/XN3t;","opcode":"event_whenflagclicked","inputs":{},"fields":{},"next":"2bPLnQYJj]pm*?:I?khj","shadow":false,"x":670.5,"y":11,"topLevel":true,"parent":null},"2bPLnQYJj]pm*?:I?khj":{"id":"2bPLnQYJj]pm*?:I?khj","opcode":"sound_setvolumeto","inputs":{"VOLUME":{"name":"VOLUME","block":"3Ta/k?.*T/,0I(e7_aC[","shadow":"3Ta/k?.*T/,0I(e7_aC["}},"fields":{},"next":"m;jn7{tsh:Jv[E6CQh[/","shadow":false,"parent":"+M8;.D~|?HC;a~/XN3t;"},"3Ta/k?.*T/,0I(e7_aC[":{"id":"3Ta/k?.*T/,0I(e7_aC[","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":25}},"next":null,"topLevel":false,"parent":"2bPLnQYJj]pm*?:I?khj","shadow":true},"m;jn7{tsh:Jv[E6CQh[/":{"id":"m;jn7{tsh:Jv[E6CQh[/","opcode":"control_forever","inputs":{"SUBSTACK":{"name":"SUBSTACK","block":")Z07}:0g~RGd,s~U)~kB","shadow":null}},"fields":{},"next":null,"shadow":false,"parent":"2bPLnQYJj]pm*?:I?khj"},")Z07}:0g~RGd,s~U)~kB":{"id":")Z07}:0g~RGd,s~U)~kB","opcode":"sound_playuntildone","inputs":{"SOUND_MENU":{"name":"SOUND_MENU","block":"}p)z*3Uq7ehzKKf:~o3v","shadow":"}p)z*3Uq7ehzKKf:~o3v"}},"fields":{},"next":null,"shadow":false,"parent":"m;jn7{tsh:Jv[E6CQh[/"},"}p)z*3Uq7ehzKKf:~o3v":{"id":"}p)z*3Uq7ehzKKf:~o3v","opcode":"sound_sounds_menu","inputs":{},"fields":{"SOUND_MENU":{"name":"SOUND_MENU","value":"mp3_missy.mp3"}},"next":null,"topLevel":false,"parent":")Z07}:0g~RGd,s~U)~kB","shadow":true}},"variables":{},"lists":{},"costumes":[{"name":"rays","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"skinId":0,"md5ext":"87e963282db9e020e8c4d075891ea12b.svg"},{"name":"backdrop1","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":7,"md5ext":"34d0806e6741143d4fe96670a5eaa285.png"},{"name":"twirl2","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":8,"md5ext":"dbf20d7d4dbcc82d427828bbffde5d8d.png"}],"sounds":[{"name":"mp3_missy.mp3","format":"adpcm","rate":22050,"sampleCount":5122944,"soundID":-1,"md5":"2c37fef717622a964ce78e84bfbece24.wav"}]},{"id":"@ISBh_`?t1bOJ:.LL~3C","name":"DINO GIF","isStage":false,"x":151,"y":-71,"size":166.85922210054798,"direction":180,"draggable":false,"currentCostume":10,"costume":{"name":"dino_0010_Layer-6","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":13,"md5ext":"214be4e1341a9a49cfcc11c45c14e0e3.png"},"visible":false,"rotationStyle":"don't rotate","blocks":{"#;CdU0p1-8k9[w^#XV(u":{"id":"#;CdU0p1-8k9[w^#XV(u","opcode":"control_start_as_clone","inputs":{},"fields":{},"next":"6l~Oo(K*P-)@):?2p|G(","shadow":false,"x":234,"y":134,"topLevel":true,"parent":null},"6l~Oo(K*P-)@):?2p|G(":{"id":"6l~Oo(K*P-)@):?2p|G(","opcode":"motion_gotoxy","inputs":{"X":{"name":"X","block":"L:Qj-TF3)P[7/3(nV^*]","shadow":"}qT`4H)hBt8Jbk..R`S7"},"Y":{"name":"Y","block":"}eRncfN!fS@loj8_1ZXl","shadow":"3tp!#}gLA]cO_B/.AX%4"}},"fields":{},"next":"ld468~yG{.eVZc4S@w`W","shadow":false,"parent":"#;CdU0p1-8k9[w^#XV(u"},"L:Qj-TF3)P[7/3(nV^*]":{"id":"L:Qj-TF3)P[7/3(nV^*]","opcode":"operator_random","inputs":{"FROM":{"name":"FROM","block":"ld~5XDkWFDa?Amd=k6bW","shadow":"ld~5XDkWFDa?Amd=k6bW"},"TO":{"name":"TO","block":"K+x@j~L]9!-N,n@8Z#l`","shadow":"K+x@j~L]9!-N,n@8Z#l`"}},"fields":{},"next":null,"shadow":false,"parent":"6l~Oo(K*P-)@):?2p|G("},"ld~5XDkWFDa?Amd=k6bW":{"id":"ld~5XDkWFDa?Amd=k6bW","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":-200}},"next":null,"topLevel":false,"parent":"L:Qj-TF3)P[7/3(nV^*]","shadow":true},"K+x@j~L]9!-N,n@8Z#l`":{"id":"K+x@j~L]9!-N,n@8Z#l`","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":200}},"next":null,"topLevel":false,"parent":"L:Qj-TF3)P[7/3(nV^*]","shadow":true},"}qT`4H)hBt8Jbk..R`S7":{"id":"}qT`4H)hBt8Jbk..R`S7","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"6l~Oo(K*P-)@):?2p|G(","shadow":true},"}eRncfN!fS@loj8_1ZXl":{"id":"}eRncfN!fS@loj8_1ZXl","opcode":"operator_random","inputs":{"FROM":{"name":"FROM","block":"]^nFI;t}04+C(lEI;|@1","shadow":"]^nFI;t}04+C(lEI;|@1"},"TO":{"name":"TO","block":"Tj|#PEq7#JA%|^H!ihS%","shadow":"Tj|#PEq7#JA%|^H!ihS%"}},"fields":{},"next":null,"shadow":false,"parent":"6l~Oo(K*P-)@):?2p|G("},"]^nFI;t}04+C(lEI;|@1":{"id":"]^nFI;t}04+C(lEI;|@1","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":-114}},"next":null,"topLevel":false,"parent":"}eRncfN!fS@loj8_1ZXl","shadow":true},"Tj|#PEq7#JA%|^H!ihS%":{"id":"Tj|#PEq7#JA%|^H!ihS%","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":114}},"next":null,"topLevel":false,"parent":"}eRncfN!fS@loj8_1ZXl","shadow":true},"3tp!#}gLA]cO_B/.AX%4":{"id":"3tp!#}gLA]cO_B/.AX%4","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"6l~Oo(K*P-)@):?2p|G(","shadow":true},"ld468~yG{.eVZc4S@w`W":{"id":"ld468~yG{.eVZc4S@w`W","opcode":"looks_setsizeto","inputs":{"SIZE":{"name":"SIZE","block":"mkS,VQOXuWF.(ee3V=cZ","shadow":"_QQ^FvSp8Fa9@rrmH4[|"}},"fields":{},"next":"O+}^kn4mH_uJ*^,r8Sx.","shadow":false,"parent":"6l~Oo(K*P-)@):?2p|G("},"mkS,VQOXuWF.(ee3V=cZ":{"id":"mkS,VQOXuWF.(ee3V=cZ","opcode":"operator_mathop","inputs":{"OPERATOR":{"name":"OPERATOR","block":"K)-C:gdAKj5A3y8Hc#(e","shadow":"K)-C:gdAKj5A3y8Hc#(e"},"NUM":{"name":"NUM","block":"fe?R].BsZj~{suM/rHyn","shadow":"0p08Y@;LZSR0}`z_4s4o"}},"fields":{},"next":null,"shadow":false,"parent":"ld468~yG{.eVZc4S@w`W"},"K)-C:gdAKj5A3y8Hc#(e":{"id":"K)-C:gdAKj5A3y8Hc#(e","opcode":"operator_mathop_menu","inputs":{},"fields":{"OPERATOR":{"name":"OPERATOR","value":"sqrt"}},"next":null,"topLevel":false,"parent":"mkS,VQOXuWF.(ee3V=cZ","shadow":true},"fe?R].BsZj~{suM/rHyn":{"id":"fe?R].BsZj~{suM/rHyn","opcode":"operator_add","inputs":{"NUM1":{"name":"NUM1","block":"P;n47a4j9,n;jU^FJ*JH","shadow":"ZbQYAd}A`|4]M,Y|e0Cv"},"NUM2":{"name":"NUM2","block":"iMtB?%lO_r4|aQJ}h#,3","shadow":"8q4Um6]-t@`p0cY{5u/2"}},"fields":{},"next":null,"shadow":false,"parent":"mkS,VQOXuWF.(ee3V=cZ"},"P;n47a4j9,n;jU^FJ*JH":{"id":"P;n47a4j9,n;jU^FJ*JH","opcode":"operator_multiply","inputs":{"NUM1":{"name":"NUM1","block":"X4`FPG|)JR^_CJ.S;u8{","shadow":")o=P{gJ0m;nU20x4M/M5"},"NUM2":{"name":"NUM2","block":"eKGS?+y?pX.pZZ3qx,{I","shadow":"rM%zS%LxHG^H`a1jBjQ("}},"fields":{},"next":null,"shadow":false,"parent":"fe?R].BsZj~{suM/rHyn"},"X4`FPG|)JR^_CJ.S;u8{":{"id":"X4`FPG|)JR^_CJ.S;u8{","opcode":"motion_xposition","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"P;n47a4j9,n;jU^FJ*JH"},")o=P{gJ0m;nU20x4M/M5":{"id":")o=P{gJ0m;nU20x4M/M5","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"P;n47a4j9,n;jU^FJ*JH","shadow":true},"eKGS?+y?pX.pZZ3qx,{I":{"id":"eKGS?+y?pX.pZZ3qx,{I","opcode":"motion_xposition","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"P;n47a4j9,n;jU^FJ*JH"},"rM%zS%LxHG^H`a1jBjQ(":{"id":"rM%zS%LxHG^H`a1jBjQ(","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"P;n47a4j9,n;jU^FJ*JH","shadow":true},"ZbQYAd}A`|4]M,Y|e0Cv":{"id":"ZbQYAd}A`|4]M,Y|e0Cv","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"fe?R].BsZj~{suM/rHyn","shadow":true},"iMtB?%lO_r4|aQJ}h#,3":{"id":"iMtB?%lO_r4|aQJ}h#,3","opcode":"operator_multiply","inputs":{"NUM1":{"name":"NUM1","block":"vK;Sl+lUa5#n`0;8_57i","shadow":"8K_TE);KCR*F+J=HPCW="},"NUM2":{"name":"NUM2","block":"d:,mr_K3A0LCTjIwsQ+W","shadow":"A{@q-Y:VVxEldYH+G28~"}},"fields":{},"next":null,"shadow":false,"parent":"fe?R].BsZj~{suM/rHyn"},"vK;Sl+lUa5#n`0;8_57i":{"id":"vK;Sl+lUa5#n`0;8_57i","opcode":"motion_yposition","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"iMtB?%lO_r4|aQJ}h#,3"},"8K_TE);KCR*F+J=HPCW=":{"id":"8K_TE);KCR*F+J=HPCW=","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"iMtB?%lO_r4|aQJ}h#,3","shadow":true},"d:,mr_K3A0LCTjIwsQ+W":{"id":"d:,mr_K3A0LCTjIwsQ+W","opcode":"motion_yposition","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"iMtB?%lO_r4|aQJ}h#,3"},"A{@q-Y:VVxEldYH+G28~":{"id":"A{@q-Y:VVxEldYH+G28~","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"iMtB?%lO_r4|aQJ}h#,3","shadow":true},"8q4Um6]-t@`p0cY{5u/2":{"id":"8q4Um6]-t@`p0cY{5u/2","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"fe?R].BsZj~{suM/rHyn","shadow":true},"0p08Y@;LZSR0}`z_4s4o":{"id":"0p08Y@;LZSR0}`z_4s4o","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"mkS,VQOXuWF.(ee3V=cZ","shadow":true},"_QQ^FvSp8Fa9@rrmH4[|":{"id":"_QQ^FvSp8Fa9@rrmH4[|","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"ld468~yG{.eVZc4S@w`W","shadow":true},"O+}^kn4mH_uJ*^,r8Sx.":{"id":"O+}^kn4mH_uJ*^,r8Sx.","opcode":"looks_switchcostumeto","inputs":{"COSTUME":{"name":"COSTUME","block":"gsQ=q0*LFg#?._Pf)8/5","shadow":"ZLsh:H.`4k+ilKIgy9Om"}},"fields":{},"next":"=^MT[Pp3!76o,)?P^{zJ","shadow":false,"parent":"ld468~yG{.eVZc4S@w`W"},"gsQ=q0*LFg#?._Pf)8/5":{"id":"gsQ=q0*LFg#?._Pf)8/5","opcode":"operator_random","inputs":{"FROM":{"name":"FROM","block":"q_di1L-7d!WT0gdBJ-*D","shadow":"q_di1L-7d!WT0gdBJ-*D"},"TO":{"name":"TO","block":"d?;5Y(V=I1j*o+U`/+B?","shadow":"d?;5Y(V=I1j*o+U`/+B?"}},"fields":{},"next":null,"shadow":false,"parent":"O+}^kn4mH_uJ*^,r8Sx."},"q_di1L-7d!WT0gdBJ-*D":{"id":"q_di1L-7d!WT0gdBJ-*D","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"gsQ=q0*LFg#?._Pf)8/5","shadow":true},"d?;5Y(V=I1j*o+U`/+B?":{"id":"d?;5Y(V=I1j*o+U`/+B?","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"gsQ=q0*LFg#?._Pf)8/5","shadow":true},"ZLsh:H.`4k+ilKIgy9Om":{"id":"ZLsh:H.`4k+ilKIgy9Om","opcode":"looks_costume","inputs":{},"fields":{"COSTUME":{"name":"COSTUME","value":""}},"next":null,"topLevel":false,"parent":"O+}^kn4mH_uJ*^,r8Sx.","shadow":true},"=^MT[Pp3!76o,)?P^{zJ":{"id":"=^MT[Pp3!76o,)?P^{zJ","opcode":"looks_seteffectto","inputs":{"EFFECT":{"name":"EFFECT","block":"Mo)x#%D(0x7?xaT@M9b7","shadow":"Mo)x#%D(0x7?xaT@M9b7"},"VALUE":{"name":"VALUE","block":"eALyDFSL6~qe8SstZU@h","shadow":"5t4PvBKJOJB|HZqoR+ml"}},"fields":{},"next":"@avT0YYyn{=N[-iFWOc?","shadow":false,"parent":"O+}^kn4mH_uJ*^,r8Sx."},"Mo)x#%D(0x7?xaT@M9b7":{"id":"Mo)x#%D(0x7?xaT@M9b7","opcode":"looks_effectmenu","inputs":{},"fields":{"EFFECT":{"name":"EFFECT","value":"color"}},"next":null,"topLevel":false,"parent":"=^MT[Pp3!76o,)?P^{zJ","shadow":true},"eALyDFSL6~qe8SstZU@h":{"id":"eALyDFSL6~qe8SstZU@h","opcode":"operator_random","inputs":{"FROM":{"name":"FROM","block":"q.L:wC~)cn@*Cp!uCy}|","shadow":"q.L:wC~)cn@*Cp!uCy}|"},"TO":{"name":"TO","block":"+xES7g}Qr2gVkL.xbY%}","shadow":"+xES7g}Qr2gVkL.xbY%}"}},"fields":{},"next":null,"shadow":false,"parent":"=^MT[Pp3!76o,)?P^{zJ"},"q.L:wC~)cn@*Cp!uCy}|":{"id":"q.L:wC~)cn@*Cp!uCy}|","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"eALyDFSL6~qe8SstZU@h","shadow":true},"+xES7g}Qr2gVkL.xbY%}":{"id":"+xES7g}Qr2gVkL.xbY%}","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":255}},"next":null,"topLevel":false,"parent":"eALyDFSL6~qe8SstZU@h","shadow":true},"5t4PvBKJOJB|HZqoR+ml":{"id":"5t4PvBKJOJB|HZqoR+ml","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"=^MT[Pp3!76o,)?P^{zJ","shadow":true},"@avT0YYyn{=N[-iFWOc?":{"id":"@avT0YYyn{=N[-iFWOc?","opcode":"looks_show","inputs":{},"fields":{},"next":"`vr!jyW0.E6dta8DwNv4","shadow":false,"parent":"=^MT[Pp3!76o,)?P^{zJ"},"`vr!jyW0.E6dta8DwNv4":{"id":"`vr!jyW0.E6dta8DwNv4","opcode":"control_repeat","inputs":{"TIMES":{"name":"TIMES","block":"Ir.;~`tk3R#u~#5N.F8P","shadow":"Ir.;~`tk3R#u~#5N.F8P"},"SUBSTACK":{"name":"SUBSTACK","block":"Xd(MU5_A{vy=f7o3aOv;","shadow":null}},"fields":{},"next":"?*18UH)X%/FL?=-HoaFW","shadow":false,"parent":"@avT0YYyn{=N[-iFWOc?"},"Ir.;~`tk3R#u~#5N.F8P":{"id":"Ir.;~`tk3R#u~#5N.F8P","opcode":"math_whole_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":2}},"next":null,"topLevel":false,"parent":"`vr!jyW0.E6dta8DwNv4","shadow":true},"Xd(MU5_A{vy=f7o3aOv;":{"id":"Xd(MU5_A{vy=f7o3aOv;","opcode":"control_repeat","inputs":{"TIMES":{"name":"TIMES","block":":hx/sF-?usjK]g/V+)M_","shadow":":hx/sF-?usjK]g/V+)M_"},"SUBSTACK":{"name":"SUBSTACK","block":"Fcmgbh}L@,@C[lM?S65e","shadow":null}},"fields":{},"next":"hBZmg@}{YmiB4yi(@v7W","shadow":false,"parent":"`vr!jyW0.E6dta8DwNv4"},":hx/sF-?usjK]g/V+)M_":{"id":":hx/sF-?usjK]g/V+)M_","opcode":"math_whole_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":50}},"next":null,"topLevel":false,"parent":"Xd(MU5_A{vy=f7o3aOv;","shadow":true},"Fcmgbh}L@,@C[lM?S65e":{"id":"Fcmgbh}L@,@C[lM?S65e","opcode":"looks_nextcostume","inputs":{},"fields":{},"next":"rqjWA2+Txj=LLtMRwHV[","shadow":false,"parent":"Xd(MU5_A{vy=f7o3aOv;"},"rqjWA2+Txj=LLtMRwHV[":{"id":"rqjWA2+Txj=LLtMRwHV[","opcode":"looks_changeeffectby","inputs":{"EFFECT":{"name":"EFFECT","block":"Da3Re+N?LCCVwLZ0Fb/3","shadow":"Da3Re+N?LCCVwLZ0Fb/3"},"CHANGE":{"name":"CHANGE","block":"Mx1%w~K(6ww{H^u648yC","shadow":"Mx1%w~K(6ww{H^u648yC"}},"fields":{},"next":"F_lXv!E*#-GeI!e?WMRF","shadow":false,"parent":"Fcmgbh}L@,@C[lM?S65e"},"Da3Re+N?LCCVwLZ0Fb/3":{"id":"Da3Re+N?LCCVwLZ0Fb/3","opcode":"looks_effectmenu","inputs":{},"fields":{"EFFECT":{"name":"EFFECT","value":"color"}},"next":null,"topLevel":false,"parent":"rqjWA2+Txj=LLtMRwHV[","shadow":true},"Mx1%w~K(6ww{H^u648yC":{"id":"Mx1%w~K(6ww{H^u648yC","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"rqjWA2+Txj=LLtMRwHV[","shadow":true},"F_lXv!E*#-GeI!e?WMRF":{"id":"F_lXv!E*#-GeI!e?WMRF","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"w=N8X@IkId2Sh@=T96B@","shadow":"w=N8X@IkId2Sh@=T96B@"}},"fields":{},"next":null,"shadow":false,"parent":"rqjWA2+Txj=LLtMRwHV["},"w=N8X@IkId2Sh@=T96B@":{"id":"w=N8X@IkId2Sh@=T96B@","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":0.03}},"next":null,"topLevel":false,"parent":"F_lXv!E*#-GeI!e?WMRF","shadow":true},"hBZmg@}{YmiB4yi(@v7W":{"id":"hBZmg@}{YmiB4yi(@v7W","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"]+.*TT{]bS/TLaH,fC`-","shadow":"]+.*TT{]bS/TLaH,fC`-"}},"fields":{},"next":null,"shadow":false,"parent":"Xd(MU5_A{vy=f7o3aOv;"},"]+.*TT{]bS/TLaH,fC`-":{"id":"]+.*TT{]bS/TLaH,fC`-","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":0.5}},"next":null,"topLevel":false,"parent":"hBZmg@}{YmiB4yi(@v7W","shadow":true},"?*18UH)X%/FL?=-HoaFW":{"id":"?*18UH)X%/FL?=-HoaFW","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"C6RAoGQiY%,s~4-sxGIn","shadow":"C6RAoGQiY%,s~4-sxGIn"}},"fields":{},"next":"2cQ#K+HkhAdqo)E)ApFo","shadow":false,"parent":"`vr!jyW0.E6dta8DwNv4"},"C6RAoGQiY%,s~4-sxGIn":{"id":"C6RAoGQiY%,s~4-sxGIn","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":3}},"next":null,"topLevel":false,"parent":"?*18UH)X%/FL?=-HoaFW","shadow":true},"2cQ#K+HkhAdqo)E)ApFo":{"id":"2cQ#K+HkhAdqo)E)ApFo","opcode":"control_delete_this_clone","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"?*18UH)X%/FL?=-HoaFW"}},"variables":{},"lists":{},"costumes":[{"name":"dino_0000_Layer-16","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":1,"md5ext":"4e330592c1f1b80a4b182cc00cf8d6bf.png"},{"name":"dino_0001_Layer-15","bitmapResolution":2,"rotationCenterX":86,"rotationCenterY":122,"skinId":2,"md5ext":"7a68a03190a519ecc4c3e1e6a6f32f41.png"},{"name":"dino_0002_Layer-14","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":3,"md5ext":"17427d759cac720f1575a36d624d10cc.png"},{"name":"dino_0003_Layer-13","bitmapResolution":2,"rotationCenterX":92,"rotationCenterY":130,"skinId":5,"md5ext":"6563f3acffd6827bbdc86358599c6815.png"},{"name":"dino_0004_Layer-12","bitmapResolution":2,"rotationCenterX":94,"rotationCenterY":134,"skinId":4,"md5ext":"a2aca11ef43fde45cc3431402b25ba9e.png"},{"name":"dino_0005_Layer-11","bitmapResolution":2,"rotationCenterX":88,"rotationCenterY":134,"skinId":6,"md5ext":"79d431943530899519080b2b8ffe8f9f.png"},{"name":"dino_0006_Layer-10","bitmapResolution":2,"rotationCenterX":84,"rotationCenterY":130,"skinId":9,"md5ext":"67104cc6c05f895a5c108c2a920ff8d1.png"},{"name":"dino_0007_Layer-9","bitmapResolution":2,"rotationCenterX":82,"rotationCenterY":134,"skinId":10,"md5ext":"39736b1f4fb42cf59463a1bfdcba2b5d.png"},{"name":"dino_0008_Layer-8","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":11,"md5ext":"16308e806c3a594832337aeac7cf52e0.png"},{"name":"dino_0009_Layer-7","bitmapResolution":2,"rotationCenterX":86,"rotationCenterY":122,"skinId":12,"md5ext":"ecd4cfd287a4f150d45411df5722362b.png"},{"name":"dino_0010_Layer-6","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":13,"md5ext":"214be4e1341a9a49cfcc11c45c14e0e3.png"},{"name":"dino_0011_Layer-5","bitmapResolution":2,"rotationCenterX":92,"rotationCenterY":130,"skinId":18,"md5ext":"b00cf2e17ff81b5d42bb0d269362e845.png"},{"name":"dino_0012_Layer-4","bitmapResolution":2,"rotationCenterX":96,"rotationCenterY":134,"skinId":14,"md5ext":"3a25736b45392dcf740f91331aa13961.png"},{"name":"dino_0013_Layer-3","bitmapResolution":2,"rotationCenterX":86,"rotationCenterY":134,"skinId":17,"md5ext":"33276549c55db74a56090148fe1e8efa.png"},{"name":"dino_0014_Layer-2","bitmapResolution":2,"rotationCenterX":86,"rotationCenterY":130,"skinId":16,"md5ext":"5b71a9c255135caa2e7c34ce072480aa.png"},{"name":"dino_0015_Layer-1","bitmapResolution":2,"rotationCenterX":82,"rotationCenterY":134,"skinId":15,"md5ext":"4ee69e1b27147fd818144d611c138263.png"}],"sounds":[{"name":"alien creak1","format":"","rate":11025,"sampleCount":8045,"soundID":-1,"md5":"0377a7476136e5e8c780c64a4828922d.wav"}]}],"meta":{"semver":"3.0.0","vm":"0.1.0","agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"}} \ No newline at end of file diff --git a/test/integration/import_sb2.js b/test/integration/import_sb2.js index 2602783f..3b375a44 100644 --- a/test/integration/import_sb2.js +++ b/test/integration/import_sb2.js @@ -8,7 +8,7 @@ const runtime = require('../../src/engine/runtime'); const sb2 = require('../../src/serialization/sb2'); test('spec', t => { - t.type(sb2, 'function'); + t.type(sb2.deserialize, 'function'); t.end(); }); @@ -16,13 +16,15 @@ test('default', t => { // Get SB2 JSON (string) const uri = path.resolve(__dirname, '../fixtures/default.sb2'); const file = extract(uri); + const json = JSON.parse(file); // Create runtime instance & load SB2 into it const rt = new runtime(); attachTestStorage(rt); - sb2(file, rt).then(targets => { + sb2.deserialize(json, rt).then(targets => { // Test t.type(file, 'string'); + t.type(json, 'object'); t.type(rt, 'object'); t.type(targets, 'object'); diff --git a/test/unit/serialization_sb2.js b/test/unit/serialization_sb2.js index 7a0bec03..cd3294f0 100644 --- a/test/unit/serialization_sb2.js +++ b/test/unit/serialization_sb2.js @@ -16,14 +16,14 @@ test('default', t => { // Get SB2 JSON (string) const uri = path.resolve(__dirname, '../fixtures/default.sb2'); const file = extract(uri); + const json = JSON.parse(file); // Create runtime instance & load SB2 into it const rt = new Runtime(); - sb2.deserialize(file, rt).then(targets => { - console.dir(targets); - + sb2.deserialize(json, rt).then(targets => { // Test t.type(file, 'string'); + t.type(json, 'object'); t.type(rt, 'object'); t.type(targets, 'object'); diff --git a/test/unit/serialization_sb3.js b/test/unit/serialization_sb3.js index 761b0b54..a43ce204 100644 --- a/test/unit/serialization_sb3.js +++ b/test/unit/serialization_sb3.js @@ -1,19 +1,22 @@ const test = require('tap').test; const VirtualMachine = require('../../src/index'); const sb3 = require('../../src/serialization/sb3'); +const demoSb3 = require('../fixtures/demo.json'); test('serialize', t => { const vm = new VirtualMachine(); - vm.fromJSON(JSON.stringify(require('../fixtures/demo.json'))); + vm.fromJSON(JSON.stringify(demoSb3)); const result = sb3.serialize(vm.runtime); - console.dir(JSON.stringify(result)); // @todo Analyze + t.type(JSON.stringify(result), 'string'); t.end(); }); test('deserialize', t => { const vm = new VirtualMachine(); - const result = sb3.deserialize('', vm.runtime); - // @todo Analyize - t.end(); + sb3.deserialize('', vm.runtime).then(targets => { + // @todo Analyize + t.type(targets, 'object'); + t.end(); + }); }); From f3c6be28814c5ef34a3be114d53581dde00dff6b Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 28 Apr 2017 13:40:13 -0700 Subject: [PATCH 312/721] Add an I/O device to represent the Device Manager --- package.json | 2 + src/engine/runtime.js | 2 + src/io/deviceManager.js | 261 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 265 insertions(+) create mode 100644 src/io/deviceManager.js diff --git a/package.json b/package.json index 3e359b39..e009cb03 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "eslint-config-scratch": "^3.1.0", "expose-loader": "0.7.3", "gh-pages": "^0.12.0", + "got": "^5.7.1", "highlightjs": "^9.8.0", "htmlparser2": "3.9.2", "json": "^9.0.4", @@ -45,6 +46,7 @@ "scratch-render": "latest", "scratch-storage": "latest", "script-loader": "0.7.0", + "socket.io-client": "^1.7.3", "stats.js": "^0.17.0", "tap": "^10.2.0", "travis-after-all": "^1.4.4", diff --git a/src/engine/runtime.js b/src/engine/runtime.js index f57a0e9f..443d4b9f 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -5,6 +5,7 @@ const Thread = require('./thread'); // Virtual I/O devices. const Clock = require('../io/clock'); +const DeviceManager = require('../io/deviceManager'); const Keyboard = require('../io/keyboard'); const Mouse = require('../io/mouse'); @@ -141,6 +142,7 @@ class Runtime extends EventEmitter { /** @type {Object.} */ this.ioDevices = { clock: new Clock(), + deviceManager: new DeviceManager(), keyboard: new Keyboard(this), mouse: new Mouse(this) }; diff --git a/src/io/deviceManager.js b/src/io/deviceManager.js new file mode 100644 index 00000000..c603ccc9 --- /dev/null +++ b/src/io/deviceManager.js @@ -0,0 +1,261 @@ +const got = require('got'); +const io = require('socket.io-client'); +const querystring = require('querystring'); + +/** + * Internal class used by the Device Manager client to manage making a connection to a particular device. + */ +class DeviceOpener { + /** + * @return {number} - The number of milliseconds to allow before deciding a connection attempt has timed out. + */ + static get CONNECTION_TIMEOUT_MS () { + return 10 * 1000; + } + + /** + * Construct a DeviceOpener to help connect to a particular device. + * @param {DeviceManager} deviceManager - the Device Manager client which instigated this action. + * @param {function} resolve - callback to be called if the device is successfully found, connected, and opened. + * @param {function} reject - callback to be called if an error or timeout is encountered. + */ + constructor (deviceManager, resolve, reject) { + this._deviceManager = deviceManager; + this._resolve = resolve; + this._reject = reject; + } + + /** + * Attempt to open a particular device. This will cause `resolve` or `reject` to be called. + * Note that in some cases it's possible that both `resolve` and `reject` will be called. In that event, ignore all + * calls after the first. If `resolve` and `reject` are from a Promise, then the Promise will do this for you. + * @param {string} extensionName - human-readable name of the extension requesting the device + * @param {string} deviceType - the type of device to open, such as 'wedo2' + * @param {string} deviceId - the ID of the particular device to open, usually from list results + */ + open (extensionName, deviceType, deviceId) { + this._socket = /** @type {Socket} */ io(`${this._deviceManager._serverURL}/${deviceType}`); + this._deviceManager._sockets.push(this._socket); + + this._socket.on('deviceWasOpened', () => this.onDeviceWasOpened()); + this._socket.on('disconnect', () => this.onDisconnect()); + this._connectionTimeout = setTimeout(() => this.onTimeout(), DeviceOpener.CONNECTION_TIMEOUT_MS); + + this._socket.emit('open', {deviceId: deviceId, name: extensionName}); + } + + /** + * React to a 'deviceWasOpened' message from the Device Manager application. + */ + onDeviceWasOpened () { + this.clearConnectionTimeout(); + this._resolve(this._socket); + } + + /** + * React to the socket becoming disconnected. + */ + onDisconnect () { + this.removeSocket(); + this.clearConnectionTimeout(); + this._reject('device disconnected'); + } + + /** + * React to the connection timeout expiring. This could mean that the socket itself timed out, or that the Device + * Manager took too long to send a 'deviceWasOpened' message back. + */ + onTimeout () { + this.clearConnectionTimeout(); + + // `socket.disconnect()` triggers `onDisconnect` only for connected sockets + if (this._socket.connected) { + this._socket.disconnect(); + } else { + this.removeSocket(); + this._reject('connection attempt timed out'); + } + } + + /** + * Cancel the connection timeout. + */ + clearConnectionTimeout () { + if (this._connectionTimeout !== null) { + clearTimeout(this._connectionTimeout); + this._connectionTimeout = null; + } + } + + /** + * Remove the socket we were using for a now-failed connection attempt. + */ + removeSocket () { + const socketIndex = this._deviceManager._sockets.indexOf(this._socket); + if (socketIndex >= 0) { + this._deviceManager._sockets.splice(socketIndex, 1); + } + } +} + +/** + * A DeviceFinder implements the Device Manager client's `searchAndConnect` functionality. + * Use the `promise` property to access a promise for a device socket. + * Call `cancel()` to cancel the search. Once the search finds a device it cannot be canceled. + */ +class DeviceFinder { + /** + * @return {number} - the number of milliseconds to wait between search attempts (calls to 'list') + */ + static get SEARCH_RETRY_MS () { + return 1000; + } + + /** + * Construct a DeviceFinder to help find and connect to a device satisfying specific conditions. + * @param {DeviceManager} deviceManager - the Device Manager client which instigated this action. + * @param {string} extensionName - human-readable name of the extension requesting the search + * @param {string} deviceType - the type of device to list, such as 'wedo2' + * @param {object} [deviceSpec] - optional additional information about the specific devices to list + */ + constructor (deviceManager, extensionName, deviceType, deviceSpec) { + this._deviceManager = deviceManager; + this._extensionName = extensionName; + this._deviceType = deviceType; + this._deviceSpec = deviceSpec; + this._cancel = false; + this._promise = null; + this._fulfill = null; + } + + /** + * @return {Promise} - A promise for a device socket. + */ + get promise () { + return this._promise; + } + + /** + * Start searching for a device. + */ + start () { + this._promise = new Promise((fulfill, reject) => { + this._fulfill = fulfill; + this._reject = reject; + this._getList(); + }); + } + + /** + * Cancel the search for a device. Effective only before the promise resolves. + */ + cancel () { + this._cancel = true; + this._reject('canceled'); + } + + /** + * Fetch the list of devices matching the parameters provided in the constructor. + * @private + */ + _getList () { + this._deviceManager + .list(this._extensionName, this._deviceType, this._deviceSpec) + .then(listResult => this._listResultHandler(listResult)); + } + + /** + * Handle the list of devices returned by the Device Manager. + * @param {Array} listResult - an array of device information objects. + * @private + */ + _listResultHandler (listResult) { + if (this._cancel) { + return; + } + + if (listResult && listResult.length > 0) { + for (const deviceInfo of listResult) { + if (!deviceInfo.connected) { + this._fulfill(this._deviceManager.open(this._extensionName, this._deviceType, deviceInfo.id)); + return; + } + } + } + + setTimeout(() => this._getList(), DeviceFinder.SEARCH_RETRY_MS); + } +} + +/** + * A Scratch 3.0 "I/O Device" representing a client for the Scratch Device Manager. + */ +class DeviceManager { + /** + * @return {string} - The default Scratch Device Manager connection URL. + */ + static get DEFAULT_SERVER_URL () { + return 'https://device-manager.scratch.mit.edu:3030'; + } + + constructor () { + this._serverURL = DeviceManager.DEFAULT_SERVER_URL; + this._isConnected = true; + this._sockets = []; + } + + /** + * @return {boolean} - True if there is no known problem connecting to the Scratch Device Manager, false otherwise. + */ + get isConnected () { + return this._isConnected; + } + + /** + * High-level request to find and connect to a device satisfying the specified characteristics. + * This function will repeatedly call list() until the list is non-empty, then it will open() the first suitable + * item in the list and provide the socket for that device. + * @todo Offer a way to filter results. See the Scratch 2.0 PicoBoard extension for details on why that's important. + * @param {string} extensionName - human-readable name of the extension requesting the search + * @param {string} deviceType - the type of device to list, such as 'wedo2' + * @param {object} [deviceSpec] - optional additional information about the specific devices to list + * @return {DeviceFinder} - An object providing a Promise for an opened device and a way to cancel the search. + */ + searchAndConnect (extensionName, deviceType, deviceSpec) { + const finder = new DeviceFinder(this, extensionName, deviceType, deviceSpec); + finder.start(); + return finder; + } + + /** + * Request a list of available devices. + * @param {string} extensionName - human-readable name of the extension requesting the list + * @param {string} deviceType - the type of device to list, such as 'wedo2' + * @param {object} [deviceSpec] - optional additional information about the specific devices to list + * @return {Promise} - A Promise for an Array of available devices. + */ + list (extensionName, deviceType, deviceSpec) { + const queryObject = { + name: extensionName + }; + if (deviceSpec) queryObject.spec = deviceSpec; + const url = `${this._serverURL}/${encodeURIComponent(deviceType)}/list?${querystring.stringify(queryObject)}`; + return got(url).then(response => JSON.parse(response.body)); + } + + /** + * Attempt to open a particular device. + * @param {string} extensionName - human-readable name of the extension requesting the device + * @param {string} deviceType - the type of device to open, such as 'wedo2' + * @param {string} deviceId - the ID of the particular device to open, usually from list results + * @return {Promise} - A Promise for a Socket which can be used to communicate with the device + */ + open (extensionName, deviceType, deviceId) { + return new Promise((resolve, reject) => { + const opener = new DeviceOpener(this, resolve, reject); + opener.open(extensionName, deviceType, deviceId); + }); + } +} + +module.exports = DeviceManager; From 9a9c509e7657e3d24a3c2f53bab95f4a4cdff143 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 28 Apr 2017 14:50:57 -0700 Subject: [PATCH 313/721] Implement a few tests for io/deviceManager --- src/io/deviceManager.js | 5 +++- test/unit/io_deviceManager.js | 46 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 test/unit/io_deviceManager.js diff --git a/src/io/deviceManager.js b/src/io/deviceManager.js index c603ccc9..3895af34 100644 --- a/src/io/deviceManager.js +++ b/src/io/deviceManager.js @@ -161,7 +161,10 @@ class DeviceFinder { _getList () { this._deviceManager .list(this._extensionName, this._deviceType, this._deviceSpec) - .then(listResult => this._listResultHandler(listResult)); + .then( + listResult => this._listResultHandler(listResult), + () => this._listResultHandler(null) + ); } /** diff --git a/test/unit/io_deviceManager.js b/test/unit/io_deviceManager.js new file mode 100644 index 00000000..89127e31 --- /dev/null +++ b/test/unit/io_deviceManager.js @@ -0,0 +1,46 @@ +const test = require('tap').test; +const DeviceManager = require('../../src/io/deviceManager'); + +test('spec', t => { + const deviceManager = new DeviceManager(); + + t.type(DeviceManager, 'function'); + t.type(deviceManager, 'object'); + t.type(deviceManager.list, 'function'); + t.type(deviceManager.open, 'function'); + t.type(deviceManager.searchAndConnect, 'function'); + t.type(deviceManager.isConnected, 'boolean'); + t.end(); +}); + +test('default connected', t => { + const deviceManager = new DeviceManager(); + + t.strictEqual(deviceManager.isConnected, true); + t.end(); +}); + +test('cancel searchAndConnect', t => { + const deviceManager = new DeviceManager(); + + const finder = deviceManager.searchAndConnect('test extension', 'test device'); + + let resolved = false; + let rejected = false; + const testPromise = finder.promise + .then( + () => { + resolved = true; + }, + () => { + rejected = true; + } + ) + .then(() => { + t.strictEqual(resolved, false); + t.strictEqual(rejected, true); + }); + finder.cancel(); + + return testPromise; +}); From 96c74867b50eeda3459d0a8c6c2df695bed73709 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 28 Apr 2017 15:06:42 -0700 Subject: [PATCH 314/721] Remove test for canceling a search When no Device Manager instance is present this test takes a long time and can cause timeouts. We'll need to fake the HTTP requests if we want to do this sort of test reliably. --- test/unit/io_deviceManager.js | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/test/unit/io_deviceManager.js b/test/unit/io_deviceManager.js index 89127e31..f013ef5d 100644 --- a/test/unit/io_deviceManager.js +++ b/test/unit/io_deviceManager.js @@ -19,28 +19,3 @@ test('default connected', t => { t.strictEqual(deviceManager.isConnected, true); t.end(); }); - -test('cancel searchAndConnect', t => { - const deviceManager = new DeviceManager(); - - const finder = deviceManager.searchAndConnect('test extension', 'test device'); - - let resolved = false; - let rejected = false; - const testPromise = finder.promise - .then( - () => { - resolved = true; - }, - () => { - rejected = true; - } - ) - .then(() => { - t.strictEqual(resolved, false); - t.strictEqual(rejected, true); - }); - finder.cancel(); - - return testPromise; -}); From 44ae4fed54c1df720bcdb00a29658727e0cccb67 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Mon, 1 May 2017 15:24:07 -0700 Subject: [PATCH 315/721] Minor-pin all Scratch 3.0 prerelease modules Pin these modules to 0.*-prerelease.* to avoid problems when we eventually bump to 1.x --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 3e359b39..b31d30af 100644 --- a/package.json +++ b/package.json @@ -40,10 +40,10 @@ "lodash.defaultsdeep": "4.6.0", "minilog": "3.1.0", "promise": "7.1.1", - "scratch-audio": "latest", - "scratch-blocks": "latest", - "scratch-render": "latest", - "scratch-storage": "latest", + "scratch-audio": "^0.1.0-prerelease.0", + "scratch-blocks": "^0.1.0-prerelease.0", + "scratch-render": "^0.1.0-prerelease.0", + "scratch-storage": "^0.0.1-prerelease.0", "script-loader": "0.7.0", "stats.js": "^0.17.0", "tap": "^10.2.0", From e096f4348763cdf6bc31ef84db238ff3d58a42b3 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Wed, 3 May 2017 16:26:31 -0700 Subject: [PATCH 316/721] DeviceManager: add more jsdoc, remove `_sockets` It turns out we don't need the Device Manager to centrally track each socket, so this change removes the Device Manager's `_sockets` property and related code. --- src/io/deviceManager.js | 99 +++++++++++++++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 14 deletions(-) diff --git a/src/io/deviceManager.js b/src/io/deviceManager.js index 3895af34..90b1fd23 100644 --- a/src/io/deviceManager.js +++ b/src/io/deviceManager.js @@ -20,9 +20,40 @@ class DeviceOpener { * @param {function} reject - callback to be called if an error or timeout is encountered. */ constructor (deviceManager, resolve, reject) { + /** + * The DeviceManager client which wants to open a device. + * @type {DeviceManager} + * @private + */ this._deviceManager = deviceManager; + + /** + * Callback to be called if the device is successfully found, connected, and opened. + * @type {Function} + * @private + */ this._resolve = resolve; + + /** + * Callback to be called if an error or timeout is encountered. + * @type {Function} + * @private + */ this._reject = reject; + + /** + * The socket for the device being opened. + * @type {Socket} + * @private + */ + this._socket = null; + + /** + * If this timeout expires before a successful connection, the connection attempt will be canceled. + * @type {Object} + * @private + */ + this._connectionTimeout = null; } /** @@ -34,8 +65,7 @@ class DeviceOpener { * @param {string} deviceId - the ID of the particular device to open, usually from list results */ open (extensionName, deviceType, deviceId) { - this._socket = /** @type {Socket} */ io(`${this._deviceManager._serverURL}/${deviceType}`); - this._deviceManager._sockets.push(this._socket); + this._socket = io(`${this._deviceManager._serverURL}/${deviceType}`); this._socket.on('deviceWasOpened', () => this.onDeviceWasOpened()); this._socket.on('disconnect', () => this.onDisconnect()); @@ -86,16 +116,6 @@ class DeviceOpener { this._connectionTimeout = null; } } - - /** - * Remove the socket we were using for a now-failed connection attempt. - */ - removeSocket () { - const socketIndex = this._deviceManager._sockets.indexOf(this._socket); - if (socketIndex >= 0) { - this._deviceManager._sockets.splice(socketIndex, 1); - } - } } /** @@ -115,16 +135,57 @@ class DeviceFinder { * Construct a DeviceFinder to help find and connect to a device satisfying specific conditions. * @param {DeviceManager} deviceManager - the Device Manager client which instigated this action. * @param {string} extensionName - human-readable name of the extension requesting the search - * @param {string} deviceType - the type of device to list, such as 'wedo2' + * @param {string} deviceType - the type of device to find, such as 'wedo2'. * @param {object} [deviceSpec] - optional additional information about the specific devices to list */ constructor (deviceManager, extensionName, deviceType, deviceSpec) { + /** + * The Device Manager client which wants to find a device. + * @type {DeviceManager} + * @private + */ this._deviceManager = deviceManager; + + /** + * The human-readable name of the extension requesting the search. + * @type {string} + * @private + */ this._extensionName = extensionName; + + /** + * The type of device to find, such as 'wedo2'. + * @type {string} + * @private + */ this._deviceType = deviceType; + + /** + * Optional additional information about the specific devices to list. + * @type {Object} + * @private + */ this._deviceSpec = deviceSpec; + + /** + * Flag indicating that the search should be canceled. + * @type {boolean} + * @private + */ this._cancel = false; + + /** + * The promise representing this search's results. + * @type {Promise} + * @private + */ this._promise = null; + + /** + * The fulfillment function for `this._promise`. + * @type {Function} + * @private + */ this._fulfill = null; } @@ -202,9 +263,19 @@ class DeviceManager { } constructor () { + /** + * The URL this client will use for Device Manager communication both HTTP(S) and WS(S). + * @type {string} + * @private + */ this._serverURL = DeviceManager.DEFAULT_SERVER_URL; + + /** + * True if there is no known problem connecting to the Scratch Device Manager, false otherwise. + * @type {boolean} + * @private + */ this._isConnected = true; - this._sockets = []; } /** From c5cf6d9b40d1da4fd506ecd7c5247a1f20daa5c2 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 4 May 2017 12:29:10 -0400 Subject: [PATCH 317/721] Use asset id reference on sounds --- src/import/load-sound.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/import/load-sound.js b/src/import/load-sound.js index f54f5fe3..a308e151 100644 --- a/src/import/load-sound.js +++ b/src/import/load-sound.js @@ -19,10 +19,17 @@ const loadSound = function (sound, runtime) { } const idParts = sound.md5.split('.'); const md5 = idParts[0]; - return runtime.storage.load(runtime.storage.AssetType.Sound, md5).then(soundAsset => { - sound.data = soundAsset.data; - return runtime.audioEngine.decodeSound(sound).then(() => sound); - }); + return runtime.storage.load(runtime.storage.AssetType.Sound, md5) + .then(soundAsset => { + sound.assetId = soundAsset.assetId; + sound.assetType = runtime.storage.AssetType.Sound; + return runtime.audioEngine.decodeSound(Object.assign( + {}, + sound, + {data: soundAsset.data} + )); + }) + .then(() => sound); }; module.exports = loadSound; From 2181e51c38108479b2b471f2b44b371556f7a7fb Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 4 May 2017 12:29:28 -0400 Subject: [PATCH 318/721] Don't store costume data uris on costumes --- src/import/load-costume.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/import/load-costume.js b/src/import/load-costume.js index 83ef7800..a9323398 100644 --- a/src/import/load-costume.js +++ b/src/import/load-costume.js @@ -13,7 +13,6 @@ const log = require('../util/log'); * @returns {?Promise} - a promise which will resolve after skinId is set, or null on error. */ const loadCostume = function (md5ext, costume, runtime) { - costume.md5ext = md5ext; if (!runtime.storage) { log.error('No storage module present; cannot load costume asset: ', md5ext); return Promise.resolve(costume); @@ -31,7 +30,8 @@ const loadCostume = function (md5ext, costume, runtime) { ]; let promise = runtime.storage.load(assetType, md5).then(costumeAsset => { - costume.url = costumeAsset.encodeDataURI(); + costume.assetId = costumeAsset.assetId; + costume.assetType = assetType; return costumeAsset; }); From efe31f4825e54530cec82509bf369565ef2073db Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 4 May 2017 12:41:54 -0400 Subject: [PATCH 319/721] Fix costume deserialization --- src/serialization/sb3.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index b072be19..4477a7b5 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -75,7 +75,8 @@ const parseScratchObject = function (object, runtime) { rotationCenterX: costumeSource.rotationCenterX, rotationCenterY: costumeSource.rotationCenterY }; - return loadCostume(costumeSource.md5ext, costume, runtime); + const costumeMd5 = `${costumeSource.assetId}.${costumeSource.assetType.runtimeFormat}`; + return loadCostume(costumeMd5, costume, runtime); }); // Sounds from JSON const soundPromises = (object.sounds || []).map(soundSource => { From baee9a2793f7e80c6abfb31ede84afe9f431095d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 4 May 2017 13:03:42 -0400 Subject: [PATCH 320/721] Don't clean costume and sound lists They are already clean, since we don't store the data on them any more. --- src/sprites/rendered-target.js | 21 ++------------------- test/fixtures/demo.json | 2 +- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 1151d3fd..694d7861 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -764,29 +764,12 @@ class RenderedTarget extends Target { } - /** - * Helper method to clean out data from each asset when serializing to JSON - * @param {Array} assetList list of costumes or sounds - * @returns {Array} list with raw data removed from each asset - */ - assetListToJSON (assetList) { - const exclude = ['data', 'url']; - return assetList.map(asset => { - if (typeof asset !== 'object') return asset; - return Object.keys(asset).reduce((rAsset, prop) => { - if (exclude.indexOf(prop) === -1) rAsset[prop] = asset[prop]; - return rAsset; - }, {}); - }); - } - /** * Serialize sprite info, used when emitting events about the sprite * @returns {object} Sprite data as a simple object */ toJSON () { - const costumes = this.assetListToJSON(this.getCostumes()); - const sounds = this.assetListToJSON(this.getSounds()); + const costumes = this.getCostumes(); return { id: this.id, name: this.getName(), @@ -805,7 +788,7 @@ class RenderedTarget extends Target { variables: this.variables, lists: this.lists, costumes: costumes, - sounds: sounds + sounds: this.getSounds() }; } diff --git a/test/fixtures/demo.json b/test/fixtures/demo.json index ed8e2cb8..d54626da 100644 --- a/test/fixtures/demo.json +++ b/test/fixtures/demo.json @@ -1 +1 @@ -{"targets":[{"id":"s_QE@H1K/8hy}:|+gG%x","name":"Stage","isStage":true,"x":0,"y":0,"size":100,"direction":90,"draggable":false,"currentCostume":2,"costume":{"name":"twirl2","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":8,"md5ext":"dbf20d7d4dbcc82d427828bbffde5d8d.png"},"visible":true,"rotationStyle":"all around","blocks":{"7ypP@K@sZk@[e3]h],~|":{"id":"7ypP@K@sZk@[e3]h],~|","opcode":"event_whenflagclicked","inputs":{},"fields":{},"next":"yG|5uo#Ak+sr:az:5:eY","shadow":false,"x":25.5,"y":13.200000000000001,"topLevel":true,"parent":null},"yG|5uo#Ak+sr:az:5:eY":{"id":"yG|5uo#Ak+sr:az:5:eY","opcode":"control_forever","inputs":{"SUBSTACK":{"name":"SUBSTACK","block":"2iU#B(lcgK/gc63NBzN|","shadow":null}},"fields":{},"next":null,"shadow":false,"parent":"7ypP@K@sZk@[e3]h],~|"},"2iU#B(lcgK/gc63NBzN|":{"id":"2iU#B(lcgK/gc63NBzN|","opcode":"control_create_clone_of","inputs":{"CLONE_OPTION":{"name":"CLONE_OPTION","block":"%?Y.y*q)s4{*.qKf8lB:","shadow":"%?Y.y*q)s4{*.qKf8lB:"}},"fields":{},"next":"t}S1*5H.`;8kh{g=}vJy","shadow":false,"parent":"yG|5uo#Ak+sr:az:5:eY"},"%?Y.y*q)s4{*.qKf8lB:":{"id":"%?Y.y*q)s4{*.qKf8lB:","opcode":"control_create_clone_of_menu","inputs":{},"fields":{"CLONE_OPTION":{"name":"CLONE_OPTION","value":"DINO GIF"}},"next":null,"topLevel":false,"parent":"2iU#B(lcgK/gc63NBzN|","shadow":true},"t}S1*5H.`;8kh{g=}vJy":{"id":"t}S1*5H.`;8kh{g=}vJy","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"Uj`3=G9oI~d[C}#2:su[","shadow":"Uj`3=G9oI~d[C}#2:su["}},"fields":{},"next":null,"shadow":false,"parent":"2iU#B(lcgK/gc63NBzN|"},"Uj`3=G9oI~d[C}#2:su[":{"id":"Uj`3=G9oI~d[C}#2:su[","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":0.5}},"next":null,"topLevel":false,"parent":"t}S1*5H.`;8kh{g=}vJy","shadow":true},"S/Jj?9hjDRtY=k:.:c7p":{"id":"S/Jj?9hjDRtY=k:.:c7p","opcode":"event_whenflagclicked","inputs":{},"fields":{},"next":"`d7M;g9pr/DWO`;S#^rr","shadow":false,"x":370.5,"y":11,"topLevel":true,"parent":null},"`d7M;g9pr/DWO`;S#^rr":{"id":"`d7M;g9pr/DWO`;S#^rr","opcode":"control_forever","inputs":{"SUBSTACK":{"name":"SUBSTACK","block":"osMx/MH`NA?V4AkwQc6d","shadow":null}},"fields":{},"next":null,"shadow":false,"parent":"S/Jj?9hjDRtY=k:.:c7p"},"osMx/MH`NA?V4AkwQc6d":{"id":"osMx/MH`NA?V4AkwQc6d","opcode":"looks_changeeffectby","inputs":{"EFFECT":{"name":"EFFECT","block":"Gp@?LHeu1YD1`2)0-Y,L","shadow":"Gp@?LHeu1YD1`2)0-Y,L"},"CHANGE":{"name":"CHANGE","block":":eos6@gH{97`g]POQN@V","shadow":":eos6@gH{97`g]POQN@V"}},"fields":{},"next":"T4gQhzYc.rPpX-^7;^-)","shadow":false,"parent":"`d7M;g9pr/DWO`;S#^rr"},"Gp@?LHeu1YD1`2)0-Y,L":{"id":"Gp@?LHeu1YD1`2)0-Y,L","opcode":"looks_effectmenu","inputs":{},"fields":{"EFFECT":{"name":"EFFECT","value":"color"}},"next":null,"topLevel":false,"parent":"osMx/MH`NA?V4AkwQc6d","shadow":true},":eos6@gH{97`g]POQN@V":{"id":":eos6@gH{97`g]POQN@V","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"osMx/MH`NA?V4AkwQc6d","shadow":true},"T4gQhzYc.rPpX-^7;^-)":{"id":"T4gQhzYc.rPpX-^7;^-)","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"~cS,w!*#5R*K,3yJ9CaJ","shadow":"~cS,w!*#5R*K,3yJ9CaJ"}},"fields":{},"next":null,"shadow":false,"parent":"osMx/MH`NA?V4AkwQc6d"},"~cS,w!*#5R*K,3yJ9CaJ":{"id":"~cS,w!*#5R*K,3yJ9CaJ","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":0.01}},"next":null,"topLevel":false,"parent":"T4gQhzYc.rPpX-^7;^-)","shadow":true},"+M8;.D~|?HC;a~/XN3t;":{"id":"+M8;.D~|?HC;a~/XN3t;","opcode":"event_whenflagclicked","inputs":{},"fields":{},"next":"2bPLnQYJj]pm*?:I?khj","shadow":false,"x":670.5,"y":11,"topLevel":true,"parent":null},"2bPLnQYJj]pm*?:I?khj":{"id":"2bPLnQYJj]pm*?:I?khj","opcode":"sound_setvolumeto","inputs":{"VOLUME":{"name":"VOLUME","block":"3Ta/k?.*T/,0I(e7_aC[","shadow":"3Ta/k?.*T/,0I(e7_aC["}},"fields":{},"next":"m;jn7{tsh:Jv[E6CQh[/","shadow":false,"parent":"+M8;.D~|?HC;a~/XN3t;"},"3Ta/k?.*T/,0I(e7_aC[":{"id":"3Ta/k?.*T/,0I(e7_aC[","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":25}},"next":null,"topLevel":false,"parent":"2bPLnQYJj]pm*?:I?khj","shadow":true},"m;jn7{tsh:Jv[E6CQh[/":{"id":"m;jn7{tsh:Jv[E6CQh[/","opcode":"control_forever","inputs":{"SUBSTACK":{"name":"SUBSTACK","block":")Z07}:0g~RGd,s~U)~kB","shadow":null}},"fields":{},"next":null,"shadow":false,"parent":"2bPLnQYJj]pm*?:I?khj"},")Z07}:0g~RGd,s~U)~kB":{"id":")Z07}:0g~RGd,s~U)~kB","opcode":"sound_playuntildone","inputs":{"SOUND_MENU":{"name":"SOUND_MENU","block":"}p)z*3Uq7ehzKKf:~o3v","shadow":"}p)z*3Uq7ehzKKf:~o3v"}},"fields":{},"next":null,"shadow":false,"parent":"m;jn7{tsh:Jv[E6CQh[/"},"}p)z*3Uq7ehzKKf:~o3v":{"id":"}p)z*3Uq7ehzKKf:~o3v","opcode":"sound_sounds_menu","inputs":{},"fields":{"SOUND_MENU":{"name":"SOUND_MENU","value":"mp3_missy.mp3"}},"next":null,"topLevel":false,"parent":")Z07}:0g~RGd,s~U)~kB","shadow":true}},"variables":{},"lists":{},"costumes":[{"name":"rays","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"skinId":0,"md5ext":"87e963282db9e020e8c4d075891ea12b.svg"},{"name":"backdrop1","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":7,"md5ext":"34d0806e6741143d4fe96670a5eaa285.png"},{"name":"twirl2","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":8,"md5ext":"dbf20d7d4dbcc82d427828bbffde5d8d.png"}],"sounds":[{"name":"mp3_missy.mp3","format":"adpcm","rate":22050,"sampleCount":5122944,"soundID":-1,"md5":"2c37fef717622a964ce78e84bfbece24.wav"}]},{"id":"@ISBh_`?t1bOJ:.LL~3C","name":"DINO GIF","isStage":false,"x":151,"y":-71,"size":166.85922210054798,"direction":180,"draggable":false,"currentCostume":10,"costume":{"name":"dino_0010_Layer-6","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":13,"md5ext":"214be4e1341a9a49cfcc11c45c14e0e3.png"},"visible":false,"rotationStyle":"don't rotate","blocks":{"#;CdU0p1-8k9[w^#XV(u":{"id":"#;CdU0p1-8k9[w^#XV(u","opcode":"control_start_as_clone","inputs":{},"fields":{},"next":"6l~Oo(K*P-)@):?2p|G(","shadow":false,"x":234,"y":134,"topLevel":true,"parent":null},"6l~Oo(K*P-)@):?2p|G(":{"id":"6l~Oo(K*P-)@):?2p|G(","opcode":"motion_gotoxy","inputs":{"X":{"name":"X","block":"L:Qj-TF3)P[7/3(nV^*]","shadow":"}qT`4H)hBt8Jbk..R`S7"},"Y":{"name":"Y","block":"}eRncfN!fS@loj8_1ZXl","shadow":"3tp!#}gLA]cO_B/.AX%4"}},"fields":{},"next":"ld468~yG{.eVZc4S@w`W","shadow":false,"parent":"#;CdU0p1-8k9[w^#XV(u"},"L:Qj-TF3)P[7/3(nV^*]":{"id":"L:Qj-TF3)P[7/3(nV^*]","opcode":"operator_random","inputs":{"FROM":{"name":"FROM","block":"ld~5XDkWFDa?Amd=k6bW","shadow":"ld~5XDkWFDa?Amd=k6bW"},"TO":{"name":"TO","block":"K+x@j~L]9!-N,n@8Z#l`","shadow":"K+x@j~L]9!-N,n@8Z#l`"}},"fields":{},"next":null,"shadow":false,"parent":"6l~Oo(K*P-)@):?2p|G("},"ld~5XDkWFDa?Amd=k6bW":{"id":"ld~5XDkWFDa?Amd=k6bW","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":-200}},"next":null,"topLevel":false,"parent":"L:Qj-TF3)P[7/3(nV^*]","shadow":true},"K+x@j~L]9!-N,n@8Z#l`":{"id":"K+x@j~L]9!-N,n@8Z#l`","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":200}},"next":null,"topLevel":false,"parent":"L:Qj-TF3)P[7/3(nV^*]","shadow":true},"}qT`4H)hBt8Jbk..R`S7":{"id":"}qT`4H)hBt8Jbk..R`S7","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"6l~Oo(K*P-)@):?2p|G(","shadow":true},"}eRncfN!fS@loj8_1ZXl":{"id":"}eRncfN!fS@loj8_1ZXl","opcode":"operator_random","inputs":{"FROM":{"name":"FROM","block":"]^nFI;t}04+C(lEI;|@1","shadow":"]^nFI;t}04+C(lEI;|@1"},"TO":{"name":"TO","block":"Tj|#PEq7#JA%|^H!ihS%","shadow":"Tj|#PEq7#JA%|^H!ihS%"}},"fields":{},"next":null,"shadow":false,"parent":"6l~Oo(K*P-)@):?2p|G("},"]^nFI;t}04+C(lEI;|@1":{"id":"]^nFI;t}04+C(lEI;|@1","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":-114}},"next":null,"topLevel":false,"parent":"}eRncfN!fS@loj8_1ZXl","shadow":true},"Tj|#PEq7#JA%|^H!ihS%":{"id":"Tj|#PEq7#JA%|^H!ihS%","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":114}},"next":null,"topLevel":false,"parent":"}eRncfN!fS@loj8_1ZXl","shadow":true},"3tp!#}gLA]cO_B/.AX%4":{"id":"3tp!#}gLA]cO_B/.AX%4","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"6l~Oo(K*P-)@):?2p|G(","shadow":true},"ld468~yG{.eVZc4S@w`W":{"id":"ld468~yG{.eVZc4S@w`W","opcode":"looks_setsizeto","inputs":{"SIZE":{"name":"SIZE","block":"mkS,VQOXuWF.(ee3V=cZ","shadow":"_QQ^FvSp8Fa9@rrmH4[|"}},"fields":{},"next":"O+}^kn4mH_uJ*^,r8Sx.","shadow":false,"parent":"6l~Oo(K*P-)@):?2p|G("},"mkS,VQOXuWF.(ee3V=cZ":{"id":"mkS,VQOXuWF.(ee3V=cZ","opcode":"operator_mathop","inputs":{"OPERATOR":{"name":"OPERATOR","block":"K)-C:gdAKj5A3y8Hc#(e","shadow":"K)-C:gdAKj5A3y8Hc#(e"},"NUM":{"name":"NUM","block":"fe?R].BsZj~{suM/rHyn","shadow":"0p08Y@;LZSR0}`z_4s4o"}},"fields":{},"next":null,"shadow":false,"parent":"ld468~yG{.eVZc4S@w`W"},"K)-C:gdAKj5A3y8Hc#(e":{"id":"K)-C:gdAKj5A3y8Hc#(e","opcode":"operator_mathop_menu","inputs":{},"fields":{"OPERATOR":{"name":"OPERATOR","value":"sqrt"}},"next":null,"topLevel":false,"parent":"mkS,VQOXuWF.(ee3V=cZ","shadow":true},"fe?R].BsZj~{suM/rHyn":{"id":"fe?R].BsZj~{suM/rHyn","opcode":"operator_add","inputs":{"NUM1":{"name":"NUM1","block":"P;n47a4j9,n;jU^FJ*JH","shadow":"ZbQYAd}A`|4]M,Y|e0Cv"},"NUM2":{"name":"NUM2","block":"iMtB?%lO_r4|aQJ}h#,3","shadow":"8q4Um6]-t@`p0cY{5u/2"}},"fields":{},"next":null,"shadow":false,"parent":"mkS,VQOXuWF.(ee3V=cZ"},"P;n47a4j9,n;jU^FJ*JH":{"id":"P;n47a4j9,n;jU^FJ*JH","opcode":"operator_multiply","inputs":{"NUM1":{"name":"NUM1","block":"X4`FPG|)JR^_CJ.S;u8{","shadow":")o=P{gJ0m;nU20x4M/M5"},"NUM2":{"name":"NUM2","block":"eKGS?+y?pX.pZZ3qx,{I","shadow":"rM%zS%LxHG^H`a1jBjQ("}},"fields":{},"next":null,"shadow":false,"parent":"fe?R].BsZj~{suM/rHyn"},"X4`FPG|)JR^_CJ.S;u8{":{"id":"X4`FPG|)JR^_CJ.S;u8{","opcode":"motion_xposition","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"P;n47a4j9,n;jU^FJ*JH"},")o=P{gJ0m;nU20x4M/M5":{"id":")o=P{gJ0m;nU20x4M/M5","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"P;n47a4j9,n;jU^FJ*JH","shadow":true},"eKGS?+y?pX.pZZ3qx,{I":{"id":"eKGS?+y?pX.pZZ3qx,{I","opcode":"motion_xposition","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"P;n47a4j9,n;jU^FJ*JH"},"rM%zS%LxHG^H`a1jBjQ(":{"id":"rM%zS%LxHG^H`a1jBjQ(","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"P;n47a4j9,n;jU^FJ*JH","shadow":true},"ZbQYAd}A`|4]M,Y|e0Cv":{"id":"ZbQYAd}A`|4]M,Y|e0Cv","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"fe?R].BsZj~{suM/rHyn","shadow":true},"iMtB?%lO_r4|aQJ}h#,3":{"id":"iMtB?%lO_r4|aQJ}h#,3","opcode":"operator_multiply","inputs":{"NUM1":{"name":"NUM1","block":"vK;Sl+lUa5#n`0;8_57i","shadow":"8K_TE);KCR*F+J=HPCW="},"NUM2":{"name":"NUM2","block":"d:,mr_K3A0LCTjIwsQ+W","shadow":"A{@q-Y:VVxEldYH+G28~"}},"fields":{},"next":null,"shadow":false,"parent":"fe?R].BsZj~{suM/rHyn"},"vK;Sl+lUa5#n`0;8_57i":{"id":"vK;Sl+lUa5#n`0;8_57i","opcode":"motion_yposition","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"iMtB?%lO_r4|aQJ}h#,3"},"8K_TE);KCR*F+J=HPCW=":{"id":"8K_TE);KCR*F+J=HPCW=","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"iMtB?%lO_r4|aQJ}h#,3","shadow":true},"d:,mr_K3A0LCTjIwsQ+W":{"id":"d:,mr_K3A0LCTjIwsQ+W","opcode":"motion_yposition","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"iMtB?%lO_r4|aQJ}h#,3"},"A{@q-Y:VVxEldYH+G28~":{"id":"A{@q-Y:VVxEldYH+G28~","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"iMtB?%lO_r4|aQJ}h#,3","shadow":true},"8q4Um6]-t@`p0cY{5u/2":{"id":"8q4Um6]-t@`p0cY{5u/2","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"fe?R].BsZj~{suM/rHyn","shadow":true},"0p08Y@;LZSR0}`z_4s4o":{"id":"0p08Y@;LZSR0}`z_4s4o","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"mkS,VQOXuWF.(ee3V=cZ","shadow":true},"_QQ^FvSp8Fa9@rrmH4[|":{"id":"_QQ^FvSp8Fa9@rrmH4[|","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"ld468~yG{.eVZc4S@w`W","shadow":true},"O+}^kn4mH_uJ*^,r8Sx.":{"id":"O+}^kn4mH_uJ*^,r8Sx.","opcode":"looks_switchcostumeto","inputs":{"COSTUME":{"name":"COSTUME","block":"gsQ=q0*LFg#?._Pf)8/5","shadow":"ZLsh:H.`4k+ilKIgy9Om"}},"fields":{},"next":"=^MT[Pp3!76o,)?P^{zJ","shadow":false,"parent":"ld468~yG{.eVZc4S@w`W"},"gsQ=q0*LFg#?._Pf)8/5":{"id":"gsQ=q0*LFg#?._Pf)8/5","opcode":"operator_random","inputs":{"FROM":{"name":"FROM","block":"q_di1L-7d!WT0gdBJ-*D","shadow":"q_di1L-7d!WT0gdBJ-*D"},"TO":{"name":"TO","block":"d?;5Y(V=I1j*o+U`/+B?","shadow":"d?;5Y(V=I1j*o+U`/+B?"}},"fields":{},"next":null,"shadow":false,"parent":"O+}^kn4mH_uJ*^,r8Sx."},"q_di1L-7d!WT0gdBJ-*D":{"id":"q_di1L-7d!WT0gdBJ-*D","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"gsQ=q0*LFg#?._Pf)8/5","shadow":true},"d?;5Y(V=I1j*o+U`/+B?":{"id":"d?;5Y(V=I1j*o+U`/+B?","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"gsQ=q0*LFg#?._Pf)8/5","shadow":true},"ZLsh:H.`4k+ilKIgy9Om":{"id":"ZLsh:H.`4k+ilKIgy9Om","opcode":"looks_costume","inputs":{},"fields":{"COSTUME":{"name":"COSTUME","value":""}},"next":null,"topLevel":false,"parent":"O+}^kn4mH_uJ*^,r8Sx.","shadow":true},"=^MT[Pp3!76o,)?P^{zJ":{"id":"=^MT[Pp3!76o,)?P^{zJ","opcode":"looks_seteffectto","inputs":{"EFFECT":{"name":"EFFECT","block":"Mo)x#%D(0x7?xaT@M9b7","shadow":"Mo)x#%D(0x7?xaT@M9b7"},"VALUE":{"name":"VALUE","block":"eALyDFSL6~qe8SstZU@h","shadow":"5t4PvBKJOJB|HZqoR+ml"}},"fields":{},"next":"@avT0YYyn{=N[-iFWOc?","shadow":false,"parent":"O+}^kn4mH_uJ*^,r8Sx."},"Mo)x#%D(0x7?xaT@M9b7":{"id":"Mo)x#%D(0x7?xaT@M9b7","opcode":"looks_effectmenu","inputs":{},"fields":{"EFFECT":{"name":"EFFECT","value":"color"}},"next":null,"topLevel":false,"parent":"=^MT[Pp3!76o,)?P^{zJ","shadow":true},"eALyDFSL6~qe8SstZU@h":{"id":"eALyDFSL6~qe8SstZU@h","opcode":"operator_random","inputs":{"FROM":{"name":"FROM","block":"q.L:wC~)cn@*Cp!uCy}|","shadow":"q.L:wC~)cn@*Cp!uCy}|"},"TO":{"name":"TO","block":"+xES7g}Qr2gVkL.xbY%}","shadow":"+xES7g}Qr2gVkL.xbY%}"}},"fields":{},"next":null,"shadow":false,"parent":"=^MT[Pp3!76o,)?P^{zJ"},"q.L:wC~)cn@*Cp!uCy}|":{"id":"q.L:wC~)cn@*Cp!uCy}|","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"eALyDFSL6~qe8SstZU@h","shadow":true},"+xES7g}Qr2gVkL.xbY%}":{"id":"+xES7g}Qr2gVkL.xbY%}","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":255}},"next":null,"topLevel":false,"parent":"eALyDFSL6~qe8SstZU@h","shadow":true},"5t4PvBKJOJB|HZqoR+ml":{"id":"5t4PvBKJOJB|HZqoR+ml","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"=^MT[Pp3!76o,)?P^{zJ","shadow":true},"@avT0YYyn{=N[-iFWOc?":{"id":"@avT0YYyn{=N[-iFWOc?","opcode":"looks_show","inputs":{},"fields":{},"next":"`vr!jyW0.E6dta8DwNv4","shadow":false,"parent":"=^MT[Pp3!76o,)?P^{zJ"},"`vr!jyW0.E6dta8DwNv4":{"id":"`vr!jyW0.E6dta8DwNv4","opcode":"control_repeat","inputs":{"TIMES":{"name":"TIMES","block":"Ir.;~`tk3R#u~#5N.F8P","shadow":"Ir.;~`tk3R#u~#5N.F8P"},"SUBSTACK":{"name":"SUBSTACK","block":"Xd(MU5_A{vy=f7o3aOv;","shadow":null}},"fields":{},"next":"?*18UH)X%/FL?=-HoaFW","shadow":false,"parent":"@avT0YYyn{=N[-iFWOc?"},"Ir.;~`tk3R#u~#5N.F8P":{"id":"Ir.;~`tk3R#u~#5N.F8P","opcode":"math_whole_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":2}},"next":null,"topLevel":false,"parent":"`vr!jyW0.E6dta8DwNv4","shadow":true},"Xd(MU5_A{vy=f7o3aOv;":{"id":"Xd(MU5_A{vy=f7o3aOv;","opcode":"control_repeat","inputs":{"TIMES":{"name":"TIMES","block":":hx/sF-?usjK]g/V+)M_","shadow":":hx/sF-?usjK]g/V+)M_"},"SUBSTACK":{"name":"SUBSTACK","block":"Fcmgbh}L@,@C[lM?S65e","shadow":null}},"fields":{},"next":"hBZmg@}{YmiB4yi(@v7W","shadow":false,"parent":"`vr!jyW0.E6dta8DwNv4"},":hx/sF-?usjK]g/V+)M_":{"id":":hx/sF-?usjK]g/V+)M_","opcode":"math_whole_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":50}},"next":null,"topLevel":false,"parent":"Xd(MU5_A{vy=f7o3aOv;","shadow":true},"Fcmgbh}L@,@C[lM?S65e":{"id":"Fcmgbh}L@,@C[lM?S65e","opcode":"looks_nextcostume","inputs":{},"fields":{},"next":"rqjWA2+Txj=LLtMRwHV[","shadow":false,"parent":"Xd(MU5_A{vy=f7o3aOv;"},"rqjWA2+Txj=LLtMRwHV[":{"id":"rqjWA2+Txj=LLtMRwHV[","opcode":"looks_changeeffectby","inputs":{"EFFECT":{"name":"EFFECT","block":"Da3Re+N?LCCVwLZ0Fb/3","shadow":"Da3Re+N?LCCVwLZ0Fb/3"},"CHANGE":{"name":"CHANGE","block":"Mx1%w~K(6ww{H^u648yC","shadow":"Mx1%w~K(6ww{H^u648yC"}},"fields":{},"next":"F_lXv!E*#-GeI!e?WMRF","shadow":false,"parent":"Fcmgbh}L@,@C[lM?S65e"},"Da3Re+N?LCCVwLZ0Fb/3":{"id":"Da3Re+N?LCCVwLZ0Fb/3","opcode":"looks_effectmenu","inputs":{},"fields":{"EFFECT":{"name":"EFFECT","value":"color"}},"next":null,"topLevel":false,"parent":"rqjWA2+Txj=LLtMRwHV[","shadow":true},"Mx1%w~K(6ww{H^u648yC":{"id":"Mx1%w~K(6ww{H^u648yC","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"rqjWA2+Txj=LLtMRwHV[","shadow":true},"F_lXv!E*#-GeI!e?WMRF":{"id":"F_lXv!E*#-GeI!e?WMRF","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"w=N8X@IkId2Sh@=T96B@","shadow":"w=N8X@IkId2Sh@=T96B@"}},"fields":{},"next":null,"shadow":false,"parent":"rqjWA2+Txj=LLtMRwHV["},"w=N8X@IkId2Sh@=T96B@":{"id":"w=N8X@IkId2Sh@=T96B@","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":0.03}},"next":null,"topLevel":false,"parent":"F_lXv!E*#-GeI!e?WMRF","shadow":true},"hBZmg@}{YmiB4yi(@v7W":{"id":"hBZmg@}{YmiB4yi(@v7W","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"]+.*TT{]bS/TLaH,fC`-","shadow":"]+.*TT{]bS/TLaH,fC`-"}},"fields":{},"next":null,"shadow":false,"parent":"Xd(MU5_A{vy=f7o3aOv;"},"]+.*TT{]bS/TLaH,fC`-":{"id":"]+.*TT{]bS/TLaH,fC`-","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":0.5}},"next":null,"topLevel":false,"parent":"hBZmg@}{YmiB4yi(@v7W","shadow":true},"?*18UH)X%/FL?=-HoaFW":{"id":"?*18UH)X%/FL?=-HoaFW","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"C6RAoGQiY%,s~4-sxGIn","shadow":"C6RAoGQiY%,s~4-sxGIn"}},"fields":{},"next":"2cQ#K+HkhAdqo)E)ApFo","shadow":false,"parent":"`vr!jyW0.E6dta8DwNv4"},"C6RAoGQiY%,s~4-sxGIn":{"id":"C6RAoGQiY%,s~4-sxGIn","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":3}},"next":null,"topLevel":false,"parent":"?*18UH)X%/FL?=-HoaFW","shadow":true},"2cQ#K+HkhAdqo)E)ApFo":{"id":"2cQ#K+HkhAdqo)E)ApFo","opcode":"control_delete_this_clone","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"?*18UH)X%/FL?=-HoaFW"}},"variables":{},"lists":{},"costumes":[{"name":"dino_0000_Layer-16","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":1,"md5ext":"4e330592c1f1b80a4b182cc00cf8d6bf.png"},{"name":"dino_0001_Layer-15","bitmapResolution":2,"rotationCenterX":86,"rotationCenterY":122,"skinId":2,"md5ext":"7a68a03190a519ecc4c3e1e6a6f32f41.png"},{"name":"dino_0002_Layer-14","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":3,"md5ext":"17427d759cac720f1575a36d624d10cc.png"},{"name":"dino_0003_Layer-13","bitmapResolution":2,"rotationCenterX":92,"rotationCenterY":130,"skinId":5,"md5ext":"6563f3acffd6827bbdc86358599c6815.png"},{"name":"dino_0004_Layer-12","bitmapResolution":2,"rotationCenterX":94,"rotationCenterY":134,"skinId":4,"md5ext":"a2aca11ef43fde45cc3431402b25ba9e.png"},{"name":"dino_0005_Layer-11","bitmapResolution":2,"rotationCenterX":88,"rotationCenterY":134,"skinId":6,"md5ext":"79d431943530899519080b2b8ffe8f9f.png"},{"name":"dino_0006_Layer-10","bitmapResolution":2,"rotationCenterX":84,"rotationCenterY":130,"skinId":9,"md5ext":"67104cc6c05f895a5c108c2a920ff8d1.png"},{"name":"dino_0007_Layer-9","bitmapResolution":2,"rotationCenterX":82,"rotationCenterY":134,"skinId":10,"md5ext":"39736b1f4fb42cf59463a1bfdcba2b5d.png"},{"name":"dino_0008_Layer-8","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":11,"md5ext":"16308e806c3a594832337aeac7cf52e0.png"},{"name":"dino_0009_Layer-7","bitmapResolution":2,"rotationCenterX":86,"rotationCenterY":122,"skinId":12,"md5ext":"ecd4cfd287a4f150d45411df5722362b.png"},{"name":"dino_0010_Layer-6","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":13,"md5ext":"214be4e1341a9a49cfcc11c45c14e0e3.png"},{"name":"dino_0011_Layer-5","bitmapResolution":2,"rotationCenterX":92,"rotationCenterY":130,"skinId":18,"md5ext":"b00cf2e17ff81b5d42bb0d269362e845.png"},{"name":"dino_0012_Layer-4","bitmapResolution":2,"rotationCenterX":96,"rotationCenterY":134,"skinId":14,"md5ext":"3a25736b45392dcf740f91331aa13961.png"},{"name":"dino_0013_Layer-3","bitmapResolution":2,"rotationCenterX":86,"rotationCenterY":134,"skinId":17,"md5ext":"33276549c55db74a56090148fe1e8efa.png"},{"name":"dino_0014_Layer-2","bitmapResolution":2,"rotationCenterX":86,"rotationCenterY":130,"skinId":16,"md5ext":"5b71a9c255135caa2e7c34ce072480aa.png"},{"name":"dino_0015_Layer-1","bitmapResolution":2,"rotationCenterX":82,"rotationCenterY":134,"skinId":15,"md5ext":"4ee69e1b27147fd818144d611c138263.png"}],"sounds":[{"name":"alien creak1","format":"","rate":11025,"sampleCount":8045,"soundID":-1,"md5":"0377a7476136e5e8c780c64a4828922d.wav"}]}],"meta":{"semver":"3.0.0","vm":"0.1.0","agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"}} \ No newline at end of file +{"targets":[{"id":"poXv!.~0@*~j*F(Om/X5","name":"Stage","isStage":true,"x":0,"y":0,"size":100,"direction":90,"draggable":false,"currentCostume":10,"costume":{"name":"backdrop2","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"skinId":7,"assetId":"7da4181ee167de7b3f5d1a91880277ff","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},"costumeCount":11,"visible":true,"rotationStyle":"all around","blocks":{"O7D.%E~TH^ULpAuHM8)@":{"id":"O7D.%E~TH^ULpAuHM8)@","opcode":"event_whenflagclicked","inputs":{},"fields":{},"next":"78+=E[P7b8!mBXMwQF`@","shadow":false,"x":138,"y":356.40000000000003,"topLevel":true,"parent":null},"78+=E[P7b8!mBXMwQF`@":{"id":"78+=E[P7b8!mBXMwQF`@","opcode":"control_forever","inputs":{"SUBSTACK":{"name":"SUBSTACK","block":"nlnk{2XqtVI*Qg,admbW","shadow":null}},"fields":{},"next":null,"shadow":false,"parent":"O7D.%E~TH^ULpAuHM8)@"},"nlnk{2XqtVI*Qg,admbW":{"id":"nlnk{2XqtVI*Qg,admbW","opcode":"looks_changeeffectby","inputs":{"EFFECT":{"name":"EFFECT","block":"*vh;qV87Q}5IP@sW=)wD","shadow":"*vh;qV87Q}5IP@sW=)wD"},"CHANGE":{"name":"CHANGE","block":"woYo[[v=PD(`R;qW{PZ%","shadow":"woYo[[v=PD(`R;qW{PZ%"}},"fields":{},"next":null,"shadow":false,"parent":"78+=E[P7b8!mBXMwQF`@"},"*vh;qV87Q}5IP@sW=)wD":{"id":"*vh;qV87Q}5IP@sW=)wD","opcode":"looks_effectmenu","inputs":{},"fields":{"EFFECT":{"name":"EFFECT","value":"color"}},"next":null,"topLevel":false,"parent":"nlnk{2XqtVI*Qg,admbW","shadow":true},"woYo[[v=PD(`R;qW{PZ%":{"id":"woYo[[v=PD(`R;qW{PZ%","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"nlnk{2XqtVI*Qg,admbW","shadow":true}},"variables":{"vy":{"name":"vy","value":-3.5165000000000006,"isCloud":false},"g":{"name":"g","value":"-.5","isCloud":false},"vx":{"name":"vx","value":1.4580000000000002,"isCloud":false},"i":{"name":"i","value":3,"isCloud":false},"d1":{"name":"d1","value":0,"isCloud":false},"x":{"name":"x","value":233.9173278872492,"isCloud":false},"acceleration":{"name":"acceleration","value":"-3","isCloud":false}},"lists":{},"costumes":[{"name":"blue sky2","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"skinId":0,"assetId":"7623e679b2baa2e7d48808614820844f","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},{"name":"woods","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"skinId":3,"assetId":"1e0f7a4c932423b13250b5cb44928109","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},{"name":"party","bitmapResolution":1,"rotationCenterX":251,"rotationCenterY":189,"skinId":2,"assetId":"108160d0e44d1c340182e31c9dc0758a","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},{"name":"boardwalk","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":6,"assetId":"de0e54cd11551566f044e7e6bc588b2c","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"blue sky3","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":179,"skinId":1,"assetId":"2024d59c1980e667e8f656134796e2c1","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},{"name":"underwater1","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":5,"assetId":"f339c6f31b11ea71d0fb8d607cec392e","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"underwater2","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":11,"assetId":"1517c21786d2d0edc2f3037408d850bd","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"stars","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":4,"assetId":"e87fed9c2a968dbeae8c94617e600e8c","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"parking-ramp","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":9,"assetId":"a7832479977c166ca0057f2a99a73305","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"backdrop1","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":10,"assetId":"f67dc7de38bac6fbb0ab68e46352521d","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"backdrop2","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"skinId":7,"assetId":"7da4181ee167de7b3f5d1a91880277ff","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}}],"sounds":[{"name":"pop","format":"","rate":11025,"sampleCount":258,"soundID":-1,"md5":"83a9787d4cb6f3b7632b4ddfebf74367.wav","data":null,"assetId":"83a9787d4cb6f3b7632b4ddfebf74367","assetType":{"contentType":"audio/x-wav","name":"Sound","runtimeFormat":"wav"}}]},{"id":"9!d8G^[6i]k*:l[W%4;l","name":"Earth","isStage":false,"x":-10,"y":10,"size":100,"direction":90,"draggable":false,"currentCostume":0,"costume":{"name":"earth","bitmapResolution":1,"rotationCenterX":72,"rotationCenterY":72,"skinId":8,"assetId":"814197522984a302972998b1a7f92d91","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},"costumeCount":1,"visible":true,"rotationStyle":"all around","blocks":{},"variables":{},"lists":{},"costumes":[{"name":"earth","bitmapResolution":1,"rotationCenterX":72,"rotationCenterY":72,"skinId":8,"assetId":"814197522984a302972998b1a7f92d91","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}}],"sounds":[{"name":"pop","format":"","rate":11025,"sampleCount":258,"soundID":-1,"md5":"83a9787d4cb6f3b7632b4ddfebf74367.wav","data":null,"assetId":"83a9787d4cb6f3b7632b4ddfebf74367","assetType":{"contentType":"audio/x-wav","name":"Sound","runtimeFormat":"wav"}}]}],"meta":{"semver":"3.0.0","vm":"0.1.0","agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"}} From 35d0544ce050bfcb642fa40e0deb52166993d54a Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 5 May 2017 10:37:29 -0700 Subject: [PATCH 321/721] Respond to review comments - Pin `got` and `socket-io.client` - Finish removing `removeSocket`-related code --- package.json | 4 ++-- src/io/deviceManager.js | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index e009cb03..b32cfb6a 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "eslint-config-scratch": "^3.1.0", "expose-loader": "0.7.3", "gh-pages": "^0.12.0", - "got": "^5.7.1", + "got": "5.7.1", "highlightjs": "^9.8.0", "htmlparser2": "3.9.2", "json": "^9.0.4", @@ -46,7 +46,7 @@ "scratch-render": "latest", "scratch-storage": "latest", "script-loader": "0.7.0", - "socket.io-client": "^1.7.3", + "socket.io-client": "1.7.3", "stats.js": "^0.17.0", "tap": "^10.2.0", "travis-after-all": "^1.4.4", diff --git a/src/io/deviceManager.js b/src/io/deviceManager.js index 90b1fd23..18e6de02 100644 --- a/src/io/deviceManager.js +++ b/src/io/deviceManager.js @@ -86,7 +86,6 @@ class DeviceOpener { * React to the socket becoming disconnected. */ onDisconnect () { - this.removeSocket(); this.clearConnectionTimeout(); this._reject('device disconnected'); } @@ -102,7 +101,6 @@ class DeviceOpener { if (this._socket.connected) { this._socket.disconnect(); } else { - this.removeSocket(); this._reject('connection attempt timed out'); } } From d0f1b77209cbe961196ef0db574ef7cf18b86ff6 Mon Sep 17 00:00:00 2001 From: Rasmus Haglund Date: Fri, 5 May 2017 21:58:18 +0200 Subject: [PATCH 322/721] Copy _customState for now until blocks have init functions --- src/sprites/rendered-target.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index e6323ec7..ad23003c 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -696,6 +696,7 @@ class RenderedTarget extends Target { newClone.effects = JSON.parse(JSON.stringify(this.effects)); newClone.variables = JSON.parse(JSON.stringify(this.variables)); newClone.lists = JSON.parse(JSON.stringify(this.lists)); + newClone._customState = JSON.parse(JSON.stringify(this._customState)); newClone.initDrawable(); newClone.updateAllDrawableProperties(); // Place behind the current target. From 706e112082f951196d4494abe97b31793c52f3d9 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 8 May 2017 09:53:16 -0400 Subject: [PATCH 323/721] Add a block container for monitors that will run in the sequencer --- src/engine/blocks.js | 26 +++++++++++++++++++++++--- src/engine/runtime.js | 20 ++++++++++++++++++-- src/engine/target.js | 2 +- src/playground/playground.js | 1 + src/virtual-machine.js | 11 +++++++++++ 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 0c90d1a6..f27d63c5 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -273,16 +273,22 @@ class Blocks { */ changeBlock (args) { // Validate - if (args.element !== 'field' && args.element !== 'mutation') return; + if (['field', 'mutation', 'checkbox'].indexOf(args.element) === -1) return; const block = this._blocks[args.id]; if (typeof block === 'undefined') return; - if (args.element === 'field') { + switch (args.element) { + case 'field': // Update block value if (!block.fields[args.name]) return; block.fields[args.name].value = args.value; - } else if (args.element === 'mutation') { + break; + case 'mutation': block.mutation = mutationAdapter(args.value); + break; + case 'checkbox': + block.isMonitored = args.value; + break; } } @@ -342,6 +348,20 @@ class Blocks { } } + + /** + * Block management: run all blocks. + * @param {!object} runtime Runtime to run all blocks in. + */ + runAllMonitored (runtime) { + Object.keys(this._blocks).forEach(blockId => { + if (this.getBlock(blockId).isMonitored) { + // @todo handle specific targets (e.g. apple x position) + runtime.toggleScript(blockId); + } + }); + } + /** * Block management: delete blocks and their associated scripts. * @param {!object} e Blockly delete event to be processed. diff --git a/src/engine/runtime.js b/src/engine/runtime.js index f57a0e9f..f2781a73 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -52,6 +52,13 @@ class Runtime extends EventEmitter { */ this.flyoutBlocks = new Blocks(); + /** + * Storage container for monitor blocks. + * These will execute on a target maybe + * @type {!Blocks} + */ + this.monitorBlocks = new Blocks(); + /** * Currently known editing target for the VM. * @type {?Target} @@ -416,8 +423,9 @@ class Runtime extends EventEmitter { /** * Toggle a script. * @param {!string} topBlockId ID of block that starts the script. + * @param {?string} optTarget target ID for target to run script on. If not supplied, uses editing target. */ - toggleScript (topBlockId) { + toggleScript (topBlockId, optTarget) { // Remove any existing thread. for (let i = 0; i < this.threads.length; i++) { if (this.threads[i].topBlock === topBlockId) { @@ -426,7 +434,7 @@ class Runtime extends EventEmitter { } } // Otherwise add it. - this._pushThread(topBlockId, this._editingTarget); + this._pushThread(topBlockId, optTarget ? optTarget : this._editingTarget); } /** @@ -632,6 +640,7 @@ class Runtime extends EventEmitter { } } this.redrawRequested = false; + this._pushMonitors(); const doneThreads = this.sequencer.stepThreads(); this._updateGlows(doneThreads); this._setThreadCount(this.threads.length + doneThreads.length); @@ -641,6 +650,13 @@ class Runtime extends EventEmitter { } } + /** + * Queue monitor blocks to sequencer to be run. + */ + _pushMonitors () { + this.monitorBlocks.runAllMonitored(this); + } + /** * Set the current editing target known by the runtime. * @param {!Target} editingTarget New editing target. diff --git a/src/engine/target.js b/src/engine/target.js index d72937f6..c4f690aa 100644 --- a/src/engine/target.js +++ b/src/engine/target.js @@ -20,7 +20,7 @@ class Target extends EventEmitter { super(); if (!blocks) { - blocks = new Blocks(this); + blocks = new Blocks(); } /** * A unique ID for this target. diff --git a/src/playground/playground.js b/src/playground/playground.js index fe06b414..c1b655a5 100644 --- a/src/playground/playground.js +++ b/src/playground/playground.js @@ -92,6 +92,7 @@ window.onload = function () { workspace.addChangeListener(vm.blockListener); const flyoutWorkspace = workspace.getFlyout().getWorkspace(); flyoutWorkspace.addChangeListener(vm.flyoutBlockListener); + flyoutWorkspace.addChangeListener(vm.monitorBlockListener); // Create FPS counter. const stats = new window.Stats(); diff --git a/src/virtual-machine.js b/src/virtual-machine.js index c0b762bd..c56e289c 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -58,6 +58,7 @@ class VirtualMachine extends EventEmitter { this.blockListener = this.blockListener.bind(this); this.flyoutBlockListener = this.flyoutBlockListener.bind(this); + this.monitorBlockListener = this.monitorBlockListener.bind(this); } /** @@ -344,6 +345,16 @@ class VirtualMachine extends EventEmitter { this.runtime.flyoutBlocks.blocklyListen(e, this.runtime); } + /** + * Handle a Blockly event for the flyout to be passed to the monitor container. + * @param {!Blockly.Event} e Any Blockly event. + */ + monitorBlockListener (e) { + if (['create', 'change'].indexOf(e.type) !== -1) { + this.runtime.monitorBlocks.blocklyListen(e, this.runtime); + } + } + /** * Set an editing target. An editor UI can use this function to switch * between editing different targets, sprites, etc. From 26a50983472d15b002b08818643e7adc43075d9c Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 8 May 2017 16:51:45 -0400 Subject: [PATCH 324/721] don't show visual report when monitor is running --- src/engine/blocks.js | 2 +- src/engine/execute.js | 2 +- src/engine/runtime.js | 15 +++++++++++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index f27d63c5..9e4a4c9e 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -194,7 +194,7 @@ class Blocks { // UI event: clicked scripts toggle in the runtime. if (e.element === 'stackclick') { if (optRuntime) { - optRuntime.toggleScript(e.blockId); + optRuntime.toggleScript(e.blockId, {showVisualReport: true}); } return; } diff --git a/src/engine/execute.js b/src/engine/execute.js index 70785955..2b66c5e7 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -85,7 +85,7 @@ const execute = function (sequencer, thread) { } else { // In a non-hat, report the value visually if necessary if // at the top of the thread stack. - if (typeof resolvedValue !== 'undefined' && thread.atStackTop()) { + if (typeof resolvedValue !== 'undefined' && thread.showVisualReport && thread.atStackTop()) { runtime.visualReport(currentBlockId, resolvedValue); } // Finished any yields. diff --git a/src/engine/runtime.js b/src/engine/runtime.js index f2781a73..34c008a1 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -369,11 +369,13 @@ class Runtime extends EventEmitter { * Create a thread and push it to the list of threads. * @param {!string} id ID of block that starts the stack. * @param {!Target} target Target to run thread on. + * @param {?boolean} optShowVisualReport true if the script should show speech bubble for its value * @return {!Thread} The newly created thread. */ - _pushThread (id, target) { + _pushThread (id, target, optShowVisualReport) { const thread = new Thread(id); thread.target = target; + thread.showVisualReport = optShowVisualReport; thread.pushStack(id); this.threads.push(thread); return thread; @@ -423,9 +425,11 @@ class Runtime extends EventEmitter { /** * Toggle a script. * @param {!string} topBlockId ID of block that starts the script. - * @param {?string} optTarget target ID for target to run script on. If not supplied, uses editing target. + * @param {?object} opts optional arguments to toggle script + * @param {?string} opts.target target ID for target to run script on. If not supplied, uses editing target. + * @param {?boolean} opts.showVisualReport true if the speech bubble should pop up on the block, false if not. */ - toggleScript (topBlockId, optTarget) { + toggleScript (topBlockId, opts) { // Remove any existing thread. for (let i = 0; i < this.threads.length; i++) { if (this.threads[i].topBlock === topBlockId) { @@ -434,7 +438,10 @@ class Runtime extends EventEmitter { } } // Otherwise add it. - this._pushThread(topBlockId, optTarget ? optTarget : this._editingTarget); + this._pushThread( + topBlockId, + opts && opts.target ? opts.target : this._editingTarget, + opts ? opts.showVisualReport : false); } /** From 35a208d1d380e720a5a83a8e783e51c028482523 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 9 May 2017 12:10:11 -0400 Subject: [PATCH 325/721] make it possible for VM to emit update monitors action --- src/engine/runtime.js | 16 ++++++++++++++++ src/virtual-machine.js | 3 +++ 2 files changed, 19 insertions(+) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 34c008a1..ae0295f8 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -233,6 +233,14 @@ class Runtime extends EventEmitter { return 'SPRITE_INFO_REPORT'; } + /** + * Event name for monitors update. + * @const {string} + */ + static get MONITORS_UPDATE () { + return 'MONITORS_UPDATE'; + } + /** * How rapidly we try to step threads by default, in ms. */ @@ -819,6 +827,14 @@ class Runtime extends EventEmitter { this.emit(Runtime.SPRITE_INFO_REPORT, target.toJSON()); } + /** + * Emit a monitor update. + * @param {!Array} monitors Array of all monitors + */ + monitorsUpdate (monitors) { + this.emit(Runtime.MONITORS_UPDATE, monitors); + } + /** * Get a target by its id. * @param {string} targetId Id of target to find. diff --git a/src/virtual-machine.js b/src/virtual-machine.js index c56e289c..ed1ab98a 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -55,6 +55,9 @@ class VirtualMachine extends EventEmitter { instance.runtime.on(Runtime.SPRITE_INFO_REPORT, spriteInfo => { instance.emit(Runtime.SPRITE_INFO_REPORT, spriteInfo); }); + instance.runtime.on(Runtime.MONITORS_UPDATE, spriteInfo => { + instance.emit(Runtime.MONITORS_UPDATE, spriteInfo); + }); this.blockListener = this.blockListener.bind(this); this.flyoutBlockListener = this.flyoutBlockListener.bind(this); From 4ae1321252e337c52e963dd65b56d564689b3b27 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 9 May 2017 17:34:13 -0400 Subject: [PATCH 326/721] indicate show visual report or update monitor on the thread --- src/engine/blocks.js | 2 +- src/engine/execute.js | 20 ++++++++++++++++++-- src/engine/runtime.js | 30 +++++++++++++++++++++--------- src/virtual-machine.js | 4 ++-- 4 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 9e4a4c9e..fb663483 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -357,7 +357,7 @@ class Blocks { Object.keys(this._blocks).forEach(blockId => { if (this.getBlock(blockId).isMonitored) { // @todo handle specific targets (e.g. apple x position) - runtime.toggleScript(blockId); + runtime.toggleScript(blockId, {updateMonitor: true}); } }); } diff --git a/src/engine/execute.js b/src/engine/execute.js index 2b66c5e7..b9e97874 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -60,6 +60,8 @@ const execute = function (sequencer, thread) { * or after a promise resolves. * @param {*} resolvedValue Value eventually returned from the primitive. */ + // @todo move this to callback attached to the thread when we have performance + // metrics (dd) const handleReport = function (resolvedValue) { thread.pushReportedValue(resolvedValue); if (isHat) { @@ -85,8 +87,22 @@ const execute = function (sequencer, thread) { } else { // In a non-hat, report the value visually if necessary if // at the top of the thread stack. - if (typeof resolvedValue !== 'undefined' && thread.showVisualReport && thread.atStackTop()) { - runtime.visualReport(currentBlockId, resolvedValue); + + if (typeof resolvedValue !== 'undefined' && thread.atStackTop()) { + if (thread.showVisualReport) { + runtime.visualReport(currentBlockId, resolvedValue); + } + + if (thread.updateMonitor) { + runtime.updateMonitors([{ + id: currentBlockId, // @todo(dd) this will collide if multiple sprites use same block + category: 'data', + label: blockContainer.getOpcode(blockContainer.getBlock(currentBlockId)), // @todo(dd) how to handle translation here? + value: String(resolvedValue), + x: 0, // @todo(dd) place below the last monitor instead + y: 0 + }]); + } } // Finished any yields. thread.status = Thread.STATUS_RUNNING; diff --git a/src/engine/runtime.js b/src/engine/runtime.js index ae0295f8..c51333e5 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -377,13 +377,22 @@ class Runtime extends EventEmitter { * Create a thread and push it to the list of threads. * @param {!string} id ID of block that starts the stack. * @param {!Target} target Target to run thread on. - * @param {?boolean} optShowVisualReport true if the script should show speech bubble for its value + * @param {?object} opts optional arguments + * @param {?boolean} opts.optShowVisualReport true if the script should show speech bubble for its value + * @param {?boolean} opts.optUpdateMonitor true if the script should show and update a monitor with its value * @return {!Thread} The newly created thread. */ - _pushThread (id, target, optShowVisualReport) { + _pushThread (id, target, opts) { + opts = Object.assign({ + showVisualReport: false, + updateMonitor: false + }, opts); + const thread = new Thread(id); thread.target = target; - thread.showVisualReport = optShowVisualReport; + thread.showVisualReport = opts.optShowVisualReport; + thread.updateMonitor = opts.updateMonitor; + thread.pushStack(id); this.threads.push(thread); return thread; @@ -436,8 +445,14 @@ class Runtime extends EventEmitter { * @param {?object} opts optional arguments to toggle script * @param {?string} opts.target target ID for target to run script on. If not supplied, uses editing target. * @param {?boolean} opts.showVisualReport true if the speech bubble should pop up on the block, false if not. + * @param {?boolean} opts.updateMonitor true if the monitor for this block should show and get updated. */ toggleScript (topBlockId, opts) { + opts = Object.assign({ + target: this._editingTarget, + showVisualReport: false, + updateMonitor: false + }, opts); // Remove any existing thread. for (let i = 0; i < this.threads.length; i++) { if (this.threads[i].topBlock === topBlockId) { @@ -446,10 +461,7 @@ class Runtime extends EventEmitter { } } // Otherwise add it. - this._pushThread( - topBlockId, - opts && opts.target ? opts.target : this._editingTarget, - opts ? opts.showVisualReport : false); + this._pushThread(topBlockId, opts.target, opts); } /** @@ -828,10 +840,10 @@ class Runtime extends EventEmitter { } /** - * Emit a monitor update. + * Emit a monitor update which adds or updates if exists the given monitors. * @param {!Array} monitors Array of all monitors */ - monitorsUpdate (monitors) { + updateMonitors (monitors) { this.emit(Runtime.MONITORS_UPDATE, monitors); } diff --git a/src/virtual-machine.js b/src/virtual-machine.js index ed1ab98a..223620cb 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -55,8 +55,8 @@ class VirtualMachine extends EventEmitter { instance.runtime.on(Runtime.SPRITE_INFO_REPORT, spriteInfo => { instance.emit(Runtime.SPRITE_INFO_REPORT, spriteInfo); }); - instance.runtime.on(Runtime.MONITORS_UPDATE, spriteInfo => { - instance.emit(Runtime.MONITORS_UPDATE, spriteInfo); + instance.runtime.on(Runtime.MONITORS_UPDATE, data => { + instance.emit(Runtime.MONITORS_UPDATE, data); }); this.blockListener = this.blockListener.bind(this); From 97bfc9035117c519cd5092ff2de8f7a045406d10 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 10 May 2017 11:22:03 -0400 Subject: [PATCH 327/721] add comment to monitor block listener filtering of events --- src/virtual-machine.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index c56e289c..b39d74d8 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -350,6 +350,8 @@ class VirtualMachine extends EventEmitter { * @param {!Blockly.Event} e Any Blockly event. */ monitorBlockListener (e) { + // Filter events by type, since monitor blocks only need to listen to these events. + // Monitor blocks shouldn't be destroyed when flyout blocks are deleted. if (['create', 'change'].indexOf(e.type) !== -1) { this.runtime.monitorBlocks.blocklyListen(e, this.runtime); } From 777d1f38243d1419ec2a0ca52a70ff9e0176ae24 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 10 May 2017 11:22:03 -0400 Subject: [PATCH 328/721] add comment to monitor block listener filtering of events --- src/virtual-machine.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 223620cb..0e5048f5 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -353,6 +353,8 @@ class VirtualMachine extends EventEmitter { * @param {!Blockly.Event} e Any Blockly event. */ monitorBlockListener (e) { + // Filter events by type, since monitor blocks only need to listen to these events. + // Monitor blocks shouldn't be destroyed when flyout blocks are deleted. if (['create', 'change'].indexOf(e.type) !== -1) { this.runtime.monitorBlocks.blocklyListen(e, this.runtime); } From 4687fe41fe7b48e4c4b1412f36e43c2f2595cbc7 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 10 May 2017 14:06:02 -0400 Subject: [PATCH 329/721] don't show green flag when monitor running --- src/engine/execute.js | 9 ++++++--- src/engine/runtime.js | 34 ++++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/engine/execute.js b/src/engine/execute.js index b9e97874..e3b591bf 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -95,11 +95,14 @@ const execute = function (sequencer, thread) { if (thread.updateMonitor) { runtime.updateMonitors([{ - id: currentBlockId, // @todo(dd) this will collide if multiple sprites use same block + // @todo(dd) this will collide if multiple sprites use same block + id: currentBlockId, category: 'data', - label: blockContainer.getOpcode(blockContainer.getBlock(currentBlockId)), // @todo(dd) how to handle translation here? + // @todo(dd) how to handle translation here? + label: blockContainer.getOpcode(blockContainer.getBlock(currentBlockId)), value: String(resolvedValue), - x: 0, // @todo(dd) place below the last monitor instead + x: 0, + // @todo(dd) place below the last monitor instead y: 0 }]); } diff --git a/src/engine/runtime.js b/src/engine/runtime.js index c51333e5..17690ee7 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -93,10 +93,10 @@ class Runtime extends EventEmitter { this._scriptGlowsPreviousFrame = []; /** - * Number of threads running during the previous frame + * Number of non-monitor threads running during the previous frame. * @type {number} */ - this._threadCount = 0; + this._nonMonitorThreadCount = 0; /** * Currently known number of clones, used to enforce clone limit. @@ -670,13 +670,31 @@ class Runtime extends EventEmitter { this._pushMonitors(); const doneThreads = this.sequencer.stepThreads(); this._updateGlows(doneThreads); - this._setThreadCount(this.threads.length + doneThreads.length); + // Add done threads so that even if a thread finishes within 1 frame, the green + // flag will still indicate that a script ran. + this._maybeEmitProjectRunStartOrStop( + this.threads.length - this._getMonitorThreadCount(this.threads) + + doneThreads.length - this._getMonitorThreadCount(doneThreads)); if (this.renderer) { // @todo: Only render when this.redrawRequested or clones rendered. this.renderer.draw(); } } + /** + * Get the number of threads in the given array that are monitor threads (threads + * that update monitor values, and don't count as running a script). + * @param {!Array.} threads The set of threads to look through. + * @return {number} The number of monitor threads in threads. + */ + _getMonitorThreadCount (threads) { + let count = 0; + threads.forEach(thread => { + if (thread.updateMonitor) count++; + }); + return count; + } + /** * Queue monitor blocks to sequencer to be run. */ @@ -769,16 +787,16 @@ class Runtime extends EventEmitter { * Emit run start/stop after each tick. Emits when `this.threads.length` goes * between non-zero and zero * - * @param {number} threadCount The new threadCount + * @param {number} nonMonitorThreadCount The new nonMonitorThreadCount */ - _setThreadCount (threadCount) { - if (this._threadCount === 0 && threadCount > 0) { + _maybeEmitProjectRunStartOrStop (nonMonitorThreadCount) { + if (this._nonMonitorThreadCount === 0 && nonMonitorThreadCount > 0) { this.emit(Runtime.PROJECT_RUN_START); } - if (this._threadCount > 0 && threadCount === 0) { + if (this._nonMonitorThreadCount > 0 && nonMonitorThreadCount === 0) { this.emit(Runtime.PROJECT_RUN_STOP); } - this._threadCount = threadCount; + this._nonMonitorThreadCount = nonMonitorThreadCount; } /** From b1355c6b0b6ac4c9ade1a9fc547eb42740fb18a2 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 10 May 2017 15:47:06 -0400 Subject: [PATCH 330/721] Fire remove monitors VM event --- src/engine/blocks.js | 9 +++++++-- src/engine/runtime.js | 18 +++++++++++++++++- src/virtual-machine.js | 7 +++++-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index fb663483..d0d2f82d 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -215,7 +215,7 @@ class Blocks { element: e.element, name: e.name, value: e.newValue - }); + }, optRuntime); break; case 'move': this.moveBlock({ @@ -270,11 +270,13 @@ class Blocks { /** * Block management: change block field values * @param {!object} args Blockly change event to be processed + * @param {?Runtime} optRuntime Optional runtime to allow changeBlock to emit actions. */ - changeBlock (args) { + changeBlock (args, optRuntime) { // Validate if (['field', 'mutation', 'checkbox'].indexOf(args.element) === -1) return; const block = this._blocks[args.id]; + let wasMonitored = block.isMonitored; if (typeof block === 'undefined') return; switch (args.element) { @@ -288,6 +290,9 @@ class Blocks { break; case 'checkbox': block.isMonitored = args.value; + if (optRuntime && wasMonitored && !block.isMonitored) { + optRuntime.removeMonitors([{id: block.id}]); + } break; } } diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 17690ee7..d94b53c0 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -241,6 +241,14 @@ class Runtime extends EventEmitter { return 'MONITORS_UPDATE'; } + /** + * Event name for monitors removed. + * @const {string} + */ + static get MONITORS_REMOVED () { + return 'MONITORS_REMOVED'; + } + /** * How rapidly we try to step threads by default, in ms. */ @@ -859,12 +867,20 @@ class Runtime extends EventEmitter { /** * Emit a monitor update which adds or updates if exists the given monitors. - * @param {!Array} monitors Array of all monitors + * @param {!Array} monitors Array of monitors to update. */ updateMonitors (monitors) { this.emit(Runtime.MONITORS_UPDATE, monitors); } + /** + * Emit a monitor update which removes if exists the given monitors. + * @param {!Array} monitors Array of monitors to remove. + */ + removeMonitors (monitors) { + this.emit(Runtime.MONITORS_REMOVED, monitors); + } + /** * Get a target by its id. * @param {string} targetId Id of target to find. diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 0e5048f5..953336d8 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -55,8 +55,11 @@ class VirtualMachine extends EventEmitter { instance.runtime.on(Runtime.SPRITE_INFO_REPORT, spriteInfo => { instance.emit(Runtime.SPRITE_INFO_REPORT, spriteInfo); }); - instance.runtime.on(Runtime.MONITORS_UPDATE, data => { - instance.emit(Runtime.MONITORS_UPDATE, data); + instance.runtime.on(Runtime.MONITORS_UPDATE, monitorList => { + instance.emit(Runtime.MONITORS_UPDATE, monitorList); + }); + instance.runtime.on(Runtime.MONITORS_REMOVED, monitorList => { + instance.emit(Runtime.MONITORS_REMOVED, monitorList); }); this.blockListener = this.blockListener.bind(this); From b2e0a632e3dd646444eda70acb455126b8f8aaf1 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 10 May 2017 17:00:08 -0400 Subject: [PATCH 331/721] add add monitor action --- src/engine/blocks.js | 15 +++++++++++++++ src/engine/execute.js | 10 +--------- src/engine/runtime.js | 16 ++++++++++++++++ src/virtual-machine.js | 3 +++ 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index d0d2f82d..95a02ec9 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -292,6 +292,21 @@ class Blocks { block.isMonitored = args.value; if (optRuntime && wasMonitored && !block.isMonitored) { optRuntime.removeMonitors([{id: block.id}]); + } else if (optRuntime && !wasMonitored && block.isMonitored) { + optRuntime.addMonitors( + // Ensure that value is not undefined, since React requires it + [{ + // @todo(dd) this will collide if multiple sprites use same block + id: block.id, + category: 'data', + // @todo(dd) how to handle translation here? + label: block.opcode, + value: '', + x: 0, + // @todo(dd) place below the last monitor instead + y: 0 + }] + ); } break; } diff --git a/src/engine/execute.js b/src/engine/execute.js index e3b591bf..0d263fba 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -87,7 +87,6 @@ const execute = function (sequencer, thread) { } else { // In a non-hat, report the value visually if necessary if // at the top of the thread stack. - if (typeof resolvedValue !== 'undefined' && thread.atStackTop()) { if (thread.showVisualReport) { runtime.visualReport(currentBlockId, resolvedValue); @@ -95,15 +94,8 @@ const execute = function (sequencer, thread) { if (thread.updateMonitor) { runtime.updateMonitors([{ - // @todo(dd) this will collide if multiple sprites use same block id: currentBlockId, - category: 'data', - // @todo(dd) how to handle translation here? - label: blockContainer.getOpcode(blockContainer.getBlock(currentBlockId)), - value: String(resolvedValue), - x: 0, - // @todo(dd) place below the last monitor instead - y: 0 + value: String(resolvedValue) }]); } } diff --git a/src/engine/runtime.js b/src/engine/runtime.js index d94b53c0..1eebdfe1 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -249,6 +249,14 @@ class Runtime extends EventEmitter { return 'MONITORS_REMOVED'; } + /** + * Event name for monitors added. + * @const {string} + */ + static get MONITORS_ADDED () { + return 'MONITORS_ADDED'; + } + /** * How rapidly we try to step threads by default, in ms. */ @@ -881,6 +889,14 @@ class Runtime extends EventEmitter { this.emit(Runtime.MONITORS_REMOVED, monitors); } + /** + * Emit a monitor update which adds if it doesn't already exist the given monitors. + * @param {!Array} monitors Array of monitors to add. + */ + addMonitors (monitors) { + this.emit(Runtime.MONITORS_ADDED, monitors); + } + /** * Get a target by its id. * @param {string} targetId Id of target to find. diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 953336d8..241dcc9d 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -61,6 +61,9 @@ class VirtualMachine extends EventEmitter { instance.runtime.on(Runtime.MONITORS_REMOVED, monitorList => { instance.emit(Runtime.MONITORS_REMOVED, monitorList); }); + instance.runtime.on(Runtime.MONITORS_ADDED, monitorList => { + instance.emit(Runtime.MONITORS_ADDED, monitorList); + }); this.blockListener = this.blockListener.bind(this); this.flyoutBlockListener = this.flyoutBlockListener.bind(this); From 635b40ec2b89e454532924564fe5a0d96d3d814e Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 11 May 2017 02:22:12 -0400 Subject: [PATCH 332/721] Log parsing errors --- src/virtual-machine.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index f5906958..fce3244a 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -191,9 +191,15 @@ class VirtualMachine extends EventEmitter { this.clear(); // Validate & parse - if (typeof json !== 'string') return; + if (typeof json !== 'string') { + log.error('Failed to parse project. Non-string supplied to fromJSON.'); + return; + } json = JSON.parse(json); - if (typeof json !== 'object') return; + if (typeof json !== 'object') { + log.error('Failed to parse project. JSON supplied to fromJSON is not an object.'); + return; + } // Establish version, deserialize, and load into runtime // @todo Support Scratch 1.4 @@ -232,9 +238,15 @@ class VirtualMachine extends EventEmitter { */ addSprite2 (json) { // Validate & parse - if (typeof json !== 'string') return; + if (typeof json !== 'string') { + log.error('Failed to parse sprite. Non-string supplied to addSprite2.'); + return; + } json = JSON.parse(json); - if (typeof json !== 'object') return; + if (typeof json !== 'object') { + log.error('Failed to parse sprite. JSON supplied to addSprite2 is not an object.'); + return; + } // Select new sprite. return sb2.deserialize(json, this.runtime, true).then(targets => { From 33fc7978684076f85fa6575770eddae0c035366d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 11 May 2017 02:24:10 -0400 Subject: [PATCH 333/721] Select the stage if the project has no sprites --- src/virtual-machine.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index fce3244a..f85caf4b 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -222,7 +222,11 @@ class VirtualMachine extends EventEmitter { } } // Select the first target for editing, e.g., the first sprite. - this.editingTarget = this.runtime.targets[1]; + if (this.runtime.targets.length > 1) { + this.editingTarget = this.runtime.targets[1]; + } else { + this.editingTarget = this.runtime.targets[0]; + } // Update the VM user's knowledge of targets and blocks on the workspace. this.emitTargetsUpdate(); From 1ea69dd5042327b4403e3c95330708ae03196dd5 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 11 May 2017 10:20:33 -0400 Subject: [PATCH 334/721] Rename functions to be less wordy --- src/engine/runtime.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 1eebdfe1..48d2da5c 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -688,9 +688,9 @@ class Runtime extends EventEmitter { this._updateGlows(doneThreads); // Add done threads so that even if a thread finishes within 1 frame, the green // flag will still indicate that a script ran. - this._maybeEmitProjectRunStartOrStop( - this.threads.length - this._getMonitorThreadCount(this.threads) + - doneThreads.length - this._getMonitorThreadCount(doneThreads)); + this._emitProjectRunStatus( + this.threads.length + doneThreads.length - + this._getMonitorThreadCount([...this.threads, ...doneThreads])); if (this.renderer) { // @todo: Only render when this.redrawRequested or clones rendered. this.renderer.draw(); @@ -805,7 +805,7 @@ class Runtime extends EventEmitter { * * @param {number} nonMonitorThreadCount The new nonMonitorThreadCount */ - _maybeEmitProjectRunStartOrStop (nonMonitorThreadCount) { + _emitProjectRunStatus (nonMonitorThreadCount) { if (this._nonMonitorThreadCount === 0 && nonMonitorThreadCount > 0) { this.emit(Runtime.PROJECT_RUN_START); } From 7841d44ccf9160c80e17cfc674edeb44749160ae Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 11 May 2017 10:33:46 -0400 Subject: [PATCH 335/721] Move the block.isMonitored below the block is undefined check --- src/engine/blocks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 95a02ec9..3d9b44d6 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -276,9 +276,9 @@ class Blocks { // Validate if (['field', 'mutation', 'checkbox'].indexOf(args.element) === -1) return; const block = this._blocks[args.id]; - let wasMonitored = block.isMonitored; if (typeof block === 'undefined') return; + let wasMonitored = block.isMonitored; switch (args.element) { case 'field': // Update block value From 620a4bd57b5780e8f1b83bbbf660102bb76f93ec Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 11 May 2017 12:47:06 -0400 Subject: [PATCH 336/721] Require scratch-storage minimum version of 0.1.0 This gives us the caching behavior required for serialization --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3e359b39..ed0e2d18 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "scratch-audio": "latest", "scratch-blocks": "latest", "scratch-render": "latest", - "scratch-storage": "latest", + "scratch-storage": "^0.1.0", "script-loader": "0.7.0", "stats.js": "^0.17.0", "tap": "^10.2.0", From 9d306962bdda1e9ed8a298cbe449d28fc2eb9b08 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Thu, 11 May 2017 12:07:46 -0700 Subject: [PATCH 337/721] Fix socket.io-client dependency problems re: build The Socket.io client library pulls in some extra dependencies in a way that isn't fully compatible with Webpack. Switching to their build output file, instead of the file specified in their `package.json`, makes those errors go away. --- src/io/deviceManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/deviceManager.js b/src/io/deviceManager.js index 18e6de02..d2d0046f 100644 --- a/src/io/deviceManager.js +++ b/src/io/deviceManager.js @@ -1,5 +1,5 @@ const got = require('got'); -const io = require('socket.io-client'); +const io = require('socket.io-client/dist/socket.io'); const querystring = require('querystring'); /** From 6b774c58f5e0b9fa99044cf036eb6eba31189ad4 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 11 May 2017 15:23:11 -0400 Subject: [PATCH 338/721] Update some comments --- src/engine/blocks.js | 14 ++++++++------ src/engine/execute.js | 5 ++--- src/engine/runtime.js | 35 +++++++++++++++++++++++------------ 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 3d9b44d6..86525627 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -270,7 +270,7 @@ class Blocks { /** * Block management: change block field values * @param {!object} args Blockly change event to be processed - * @param {?Runtime} optRuntime Optional runtime to allow changeBlock to emit actions. + * @param {?Runtime} optRuntime Optional runtime to allow changeBlock to change VM state. */ changeBlock (args, optRuntime) { // Validate @@ -291,21 +291,23 @@ class Blocks { case 'checkbox': block.isMonitored = args.value; if (optRuntime && wasMonitored && !block.isMonitored) { - optRuntime.removeMonitors([{id: block.id}]); + optRuntime.removeMonitor(block.id); } else if (optRuntime && !wasMonitored && block.isMonitored) { - optRuntime.addMonitors( + optRuntime.addMonitor( // Ensure that value is not undefined, since React requires it - [{ + { // @todo(dd) this will collide if multiple sprites use same block id: block.id, category: 'data', // @todo(dd) how to handle translation here? label: block.opcode, + // @todo(dd) for numerical values with decimals, some countries use comma value: '', x: 0, - // @todo(dd) place below the last monitor instead + // @todo(dd) Don't require sending x and y when instantiating a + // monitor. If it's not preset the GUI should decide. y: 0 - }] + } ); } break; diff --git a/src/engine/execute.js b/src/engine/execute.js index 0d263fba..a6273fc8 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -91,12 +91,11 @@ const execute = function (sequencer, thread) { if (thread.showVisualReport) { runtime.visualReport(currentBlockId, resolvedValue); } - if (thread.updateMonitor) { - runtime.updateMonitors([{ + runtime.updateMonitor({ id: currentBlockId, value: String(resolvedValue) - }]); + }); } } // Finished any yields. diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 48d2da5c..a1ab579e 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -104,6 +104,11 @@ class Runtime extends EventEmitter { */ this._cloneCounter = 0; + /** + * List of all monitors. + */ + this._monitorState = {}; + /** * Whether the project is in "turbo mode." * @type {Boolean} @@ -695,6 +700,7 @@ class Runtime extends EventEmitter { // @todo: Only render when this.redrawRequested or clones rendered. this.renderer.draw(); } + this.emit(Runtime.MONITORS_UPDATE, Object.values(this._monitorState)); } /** @@ -874,27 +880,32 @@ class Runtime extends EventEmitter { } /** - * Emit a monitor update which adds or updates if exists the given monitors. - * @param {!Array} monitors Array of monitors to update. + * Add a monitor to the state. If the monitor already exists in the state, + * overwrites it. + * @param {!object} monitor Monitor to add. */ - updateMonitors (monitors) { - this.emit(Runtime.MONITORS_UPDATE, monitors); + addMonitor (monitor) { + this._monitorState[monitor.id] = monitor; } /** - * Emit a monitor update which removes if exists the given monitors. - * @param {!Array} monitors Array of monitors to remove. + * Update a monitor in the state. Does nothing if the monitor does not already + * exist in the state. + * @param {!object} monitor Monitor to update. */ - removeMonitors (monitors) { - this.emit(Runtime.MONITORS_REMOVED, monitors); + updateMonitor (monitor) { + if (this._monitorState.hasOwnProperty(monitor.id)) { + this._monitorState[monitor.id] = Object.assign({}, this._monitorState[monitor.id], monitor); + } } /** - * Emit a monitor update which adds if it doesn't already exist the given monitors. - * @param {!Array} monitors Array of monitors to add. + * Removes a monitor from the state. Does nothing if the monitor already does + * not exist in the state. + * @param {!object} monitorId ID of the monitor to remove. */ - addMonitors (monitors) { - this.emit(Runtime.MONITORS_ADDED, monitors); + removeMonitor (monitorId) { + delete this._monitorState[monitorId]; } /** From 2a7f3edda62973301659bbb1f7d1919dc8ee34d2 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 11 May 2017 17:11:06 -0400 Subject: [PATCH 339/721] Fix some issues: 1. Monitors not running anymore if you change categories 2. Visual reports not showing 3. threads acquiring properties of old thread when restarting thread --- src/engine/execute.js | 7 ++++++- src/engine/runtime.js | 8 +++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/engine/execute.js b/src/engine/execute.js index a6273fc8..9fed7f43 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -30,7 +30,12 @@ const execute = function (sequencer, thread) { const currentBlockId = thread.peekStack(); const currentStackFrame = thread.peekStackFrame(); - let blockContainer = target.blocks; + let blockContainer; + if (thread.updateMonitor) { + blockContainer = runtime.monitorBlocks; + } else { + blockContainer = target.blocks; + } let block = blockContainer.getBlock(currentBlockId); if (typeof block === 'undefined') { blockContainer = runtime.flyoutBlocks; diff --git a/src/engine/runtime.js b/src/engine/runtime.js index a1ab579e..662d910d 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -399,8 +399,8 @@ class Runtime extends EventEmitter { * @param {!string} id ID of block that starts the stack. * @param {!Target} target Target to run thread on. * @param {?object} opts optional arguments - * @param {?boolean} opts.optShowVisualReport true if the script should show speech bubble for its value - * @param {?boolean} opts.optUpdateMonitor true if the script should show and update a monitor with its value + * @param {?boolean} opts.showVisualReport true if the script should show speech bubble for its value + * @param {?boolean} opts.updateMonitor true if the script should show and update a monitor with its value * @return {!Thread} The newly created thread. */ _pushThread (id, target, opts) { @@ -411,7 +411,7 @@ class Runtime extends EventEmitter { const thread = new Thread(id); thread.target = target; - thread.showVisualReport = opts.optShowVisualReport; + thread.showVisualReport = opts.showVisualReport; thread.updateMonitor = opts.updateMonitor; thread.pushStack(id); @@ -442,6 +442,8 @@ class Runtime extends EventEmitter { _restartThread (thread) { const newThread = new Thread(thread.topBlock); newThread.target = thread.target; + newThread.showVisualReport = thread.showVisualReport; + newThread.updateMonitor = thread.updateMonitor; newThread.pushStack(thread.topBlock); const i = this.threads.indexOf(thread); if (i > -1) { From 7a1b3eccd02f0d2bc5a65beab02c4260a2299ce8 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 11 May 2017 17:28:48 -0400 Subject: [PATCH 340/721] fix Object.values is not a function test --- src/engine/runtime.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 662d910d..a6c2b5f8 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -702,7 +702,9 @@ class Runtime extends EventEmitter { // @todo: Only render when this.redrawRequested or clones rendered. this.renderer.draw(); } - this.emit(Runtime.MONITORS_UPDATE, Object.values(this._monitorState)); + this.emit(Runtime.MONITORS_UPDATE, + Object.keys(this._monitorState).map(key => this._monitorState[key]) + ); } /** From 6ec231db9d419a760a92dbdd347778237f583a58 Mon Sep 17 00:00:00 2001 From: Wang Yu Date: Fri, 12 May 2017 14:01:37 +0800 Subject: [PATCH 341/721] Fix the next thread is skipped after clone deleted Problem: When a clone is deleted, its thread is removed from the thread list; but the index "i" in the execution for-loop will still +1, making the next thread skipped. Changes: Added a flag "isKilled" on a thread; Set the flag when a thread is removed from the thread list; reduce the "i" counter if the thread of the current loop is removed. --- src/engine/runtime.js | 1 + src/engine/sequencer.js | 3 +++ src/engine/thread.js | 6 ++++++ 3 files changed, 10 insertions(+) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 34c008a1..7f75f952 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -590,6 +590,7 @@ class Runtime extends EventEmitter { continue; } if (this.threads[i].target === target) { + this.threads[i].isKilled = true; this._removeThread(this.threads[i]); } } diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js index c8051c87..d5c79eae 100644 --- a/src/engine/sequencer.js +++ b/src/engine/sequencer.js @@ -69,6 +69,9 @@ class Sequencer { // Normal-mode thread: step. this.stepThread(activeThread); activeThread.warpTimer = null; + if (activeThread.isKilled) { + i--; // if the thread is removed from the list (killed), do not increase index + } } if (activeThread.status === Thread.STATUS_RUNNING) { numActiveThreads++; diff --git a/src/engine/thread.js b/src/engine/thread.js index 208ff1e9..3b8ce32b 100644 --- a/src/engine/thread.js +++ b/src/engine/thread.js @@ -30,6 +30,12 @@ class Thread { */ this.status = 0; /* Thread.STATUS_RUNNING */ + /** + * Whether the thread is killed in the middle of execution. + * @type {boolean} + */ + this.isKilled = false; + /** * Target of this thread. * @type {?Target} From 20337b53f23c6d47e23745e6f118d0e3875a3da6 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 12 May 2017 11:42:22 -0400 Subject: [PATCH 342/721] Stop emitting SPRITE_INFO_REPORT Instead, set the `_refreshTargets` flag, and emit a full target list at the end of the step. --- src/engine/runtime.js | 37 +++++++++++++++++++++------------- src/sprites/rendered-target.js | 14 ++++++------- src/virtual-machine.js | 37 +++++++++++++++++----------------- 3 files changed, 48 insertions(+), 40 deletions(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 34c008a1..2708ebe7 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -104,6 +104,13 @@ class Runtime extends EventEmitter { */ this._cloneCounter = 0; + /** + * Flag to emit a targets update at the end of a step. When target data + * changes, this flag is set to true. + * @type {boolean} + */ + this._refreshTargets = false; + /** * Whether the project is in "turbo mode." * @type {Boolean} @@ -226,11 +233,11 @@ class Runtime extends EventEmitter { } /** - * Event name for sprite info report. + * Event name for targets update report. * @const {string} */ - static get SPRITE_INFO_REPORT () { - return 'SPRITE_INFO_REPORT'; + static get TARGETS_UPDATE () { + return 'TARGETS_UPDATE'; } /** @@ -638,6 +645,7 @@ class Runtime extends EventEmitter { * inactive threads after each iteration. */ _step () { + this._refreshTargets = false; // Find all edge-activated hats, and add them to threads to be evaluated. for (const hatType in this._hats) { if (!this._hats.hasOwnProperty(hatType)) continue; @@ -655,6 +663,7 @@ class Runtime extends EventEmitter { // @todo: Only render when this.redrawRequested or clones rendered. this.renderer.draw(); } + if (this._refreshTargets) this.emit(Runtime.TARGETS_UPDATE); } /** @@ -673,7 +682,7 @@ class Runtime extends EventEmitter { // Script glows must be cleared. this._scriptGlowsPreviousFrame = []; this._updateGlows(); - this.spriteInfoReport(editingTarget); + this.requestTargetsUpdate(editingTarget); } /** @@ -809,16 +818,6 @@ class Runtime extends EventEmitter { this.emit(Runtime.VISUAL_REPORT, {id: blockId, value: String(value)}); } - /** - * Emit a sprite info report if the provided target is the original sprite - * @param {!Target} target Target to report sprite info for. - */ - spriteInfoReport (target) { - if (!target.isOriginal) return; - - this.emit(Runtime.SPRITE_INFO_REPORT, target.toJSON()); - } - /** * Get a target by its id. * @param {string} targetId Id of target to find. @@ -896,6 +895,16 @@ class Runtime extends EventEmitter { this.redrawRequested = true; } + /** + * Emit a targets update at the end of the step if the provided target is + * the original sprite + * @param {!Target} target Target requesting the targets update + */ + requestTargetsUpdate (target) { + if (!target.isOriginal) return; + this._refreshTargets = true; + } + /** * Set up timers to repeatedly step in a browser. */ diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 1ffc00a1..8489b37c 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -197,7 +197,7 @@ class RenderedTarget extends Target { this.y = y; } this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY); - this.runtime.spriteInfoReport(this); + this.runtime.requestTargetsUpdate(this); } /** @@ -240,7 +240,7 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } - this.runtime.spriteInfoReport(this); + this.runtime.requestTargetsUpdate(this); } /** @@ -250,7 +250,7 @@ class RenderedTarget extends Target { setDraggable (draggable) { if (this.isStage) return; this.draggable = !!draggable; - this.runtime.spriteInfoReport(this); + this.runtime.requestTargetsUpdate(this); } /** @@ -287,7 +287,7 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } - this.runtime.spriteInfoReport(this); + this.runtime.requestTargetsUpdate(this); } /** @@ -386,7 +386,7 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } - this.runtime.spriteInfoReport(this); + this.runtime.requestTargetsUpdate(this); } /** @@ -411,7 +411,7 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } - this.runtime.spriteInfoReport(this); + this.runtime.requestTargetsUpdate(this); } /** @@ -483,7 +483,7 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } - this.runtime.spriteInfoReport(this); + this.runtime.requestTargetsUpdate(this); } /** diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 719bf6cc..79c16d2f 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -19,42 +19,41 @@ class VirtualMachine extends EventEmitter { constructor () { super(); - const instance = this; /** * VM runtime, to store blocks, I/O devices, sprites/targets, etc. * @type {!Runtime} */ - instance.runtime = new Runtime(); + this.runtime = new Runtime(); /** * The "currently editing"/selected target ID for the VM. * Block events from any Blockly workspace are routed to this target. * @type {!string} */ - instance.editingTarget = null; + this.editingTarget = null; // Runtime emits are passed along as VM emits. - instance.runtime.on(Runtime.SCRIPT_GLOW_ON, glowData => { - instance.emit(Runtime.SCRIPT_GLOW_ON, glowData); + this.runtime.on(Runtime.SCRIPT_GLOW_ON, glowData => { + this.emit(Runtime.SCRIPT_GLOW_ON, glowData); }); - instance.runtime.on(Runtime.SCRIPT_GLOW_OFF, glowData => { - instance.emit(Runtime.SCRIPT_GLOW_OFF, glowData); + this.runtime.on(Runtime.SCRIPT_GLOW_OFF, glowData => { + this.emit(Runtime.SCRIPT_GLOW_OFF, glowData); }); - instance.runtime.on(Runtime.BLOCK_GLOW_ON, glowData => { - instance.emit(Runtime.BLOCK_GLOW_ON, glowData); + this.runtime.on(Runtime.BLOCK_GLOW_ON, glowData => { + this.emit(Runtime.BLOCK_GLOW_ON, glowData); }); - instance.runtime.on(Runtime.BLOCK_GLOW_OFF, glowData => { - instance.emit(Runtime.BLOCK_GLOW_OFF, glowData); + this.runtime.on(Runtime.BLOCK_GLOW_OFF, glowData => { + this.emit(Runtime.BLOCK_GLOW_OFF, glowData); }); - instance.runtime.on(Runtime.PROJECT_RUN_START, () => { - instance.emit(Runtime.PROJECT_RUN_START); + this.runtime.on(Runtime.PROJECT_RUN_START, () => { + this.emit(Runtime.PROJECT_RUN_START); }); - instance.runtime.on(Runtime.PROJECT_RUN_STOP, () => { - instance.emit(Runtime.PROJECT_RUN_STOP); + this.runtime.on(Runtime.PROJECT_RUN_STOP, () => { + this.emit(Runtime.PROJECT_RUN_STOP); }); - instance.runtime.on(Runtime.VISUAL_REPORT, visualReport => { - instance.emit(Runtime.VISUAL_REPORT, visualReport); + this.runtime.on(Runtime.VISUAL_REPORT, visualReport => { + this.emit(Runtime.VISUAL_REPORT, visualReport); }); - instance.runtime.on(Runtime.SPRITE_INFO_REPORT, spriteInfo => { - instance.emit(Runtime.SPRITE_INFO_REPORT, spriteInfo); + this.runtime.on(Runtime.TARGETS_UPDATE, () => { + this.emitTargetsUpdate(); }); this.blockListener = this.blockListener.bind(this); From 6ee1e6614cd4a0709dafbdade008fec0df8dcd29 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 15 May 2017 10:12:25 -0400 Subject: [PATCH 343/721] Remove leftover instances of MONITORS_ADDED and MONITORS_REMOVED, change updateMonitor to requestUpdateMonitor, update some todos with tracking numbers. --- src/engine/blocks.js | 12 ++++++------ src/engine/execute.js | 2 +- src/engine/runtime.js | 27 ++++++--------------------- src/virtual-machine.js | 6 ------ 4 files changed, 13 insertions(+), 34 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 86525627..ebe113f8 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -291,20 +291,20 @@ class Blocks { case 'checkbox': block.isMonitored = args.value; if (optRuntime && wasMonitored && !block.isMonitored) { - optRuntime.removeMonitor(block.id); + optRuntime.requestRemoveMonitor(block.id); } else if (optRuntime && !wasMonitored && block.isMonitored) { - optRuntime.addMonitor( + optRuntime.requestAddMonitor( // Ensure that value is not undefined, since React requires it { - // @todo(dd) this will collide if multiple sprites use same block + // @todo(vm#564) this will collide if multiple sprites use same block id: block.id, category: 'data', - // @todo(dd) how to handle translation here? + // @todo(vm#565) how to handle translation here? label: block.opcode, - // @todo(dd) for numerical values with decimals, some countries use comma + // @todo(vm#565) for numerical values with decimals, some countries use comma value: '', x: 0, - // @todo(dd) Don't require sending x and y when instantiating a + // @todo(vm#566) Don't require sending x and y when instantiating a // monitor. If it's not preset the GUI should decide. y: 0 } diff --git a/src/engine/execute.js b/src/engine/execute.js index 9fed7f43..34e2e91b 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -97,7 +97,7 @@ const execute = function (sequencer, thread) { runtime.visualReport(currentBlockId, resolvedValue); } if (thread.updateMonitor) { - runtime.updateMonitor({ + runtime.requestUpdateMonitor({ id: currentBlockId, value: String(resolvedValue) }); diff --git a/src/engine/runtime.js b/src/engine/runtime.js index a6c2b5f8..0ac3ccff 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -246,22 +246,6 @@ class Runtime extends EventEmitter { return 'MONITORS_UPDATE'; } - /** - * Event name for monitors removed. - * @const {string} - */ - static get MONITORS_REMOVED () { - return 'MONITORS_REMOVED'; - } - - /** - * Event name for monitors added. - * @const {string} - */ - static get MONITORS_ADDED () { - return 'MONITORS_ADDED'; - } - /** * How rapidly we try to step threads by default, in ms. */ @@ -400,7 +384,7 @@ class Runtime extends EventEmitter { * @param {!Target} target Target to run thread on. * @param {?object} opts optional arguments * @param {?boolean} opts.showVisualReport true if the script should show speech bubble for its value - * @param {?boolean} opts.updateMonitor true if the script should show and update a monitor with its value + * @param {?boolean} opts.updateMonitor true if the script should update a monitor value * @return {!Thread} The newly created thread. */ _pushThread (id, target, opts) { @@ -468,7 +452,7 @@ class Runtime extends EventEmitter { * @param {?object} opts optional arguments to toggle script * @param {?string} opts.target target ID for target to run script on. If not supplied, uses editing target. * @param {?boolean} opts.showVisualReport true if the speech bubble should pop up on the block, false if not. - * @param {?boolean} opts.updateMonitor true if the monitor for this block should show and get updated. + * @param {?boolean} opts.updateMonitor true if the monitor for this block should get updated. */ toggleScript (topBlockId, opts) { opts = Object.assign({ @@ -702,6 +686,7 @@ class Runtime extends EventEmitter { // @todo: Only render when this.redrawRequested or clones rendered. this.renderer.draw(); } + // @todo only emit if monitors has changed since last time. this.emit(Runtime.MONITORS_UPDATE, Object.keys(this._monitorState).map(key => this._monitorState[key]) ); @@ -888,7 +873,7 @@ class Runtime extends EventEmitter { * overwrites it. * @param {!object} monitor Monitor to add. */ - addMonitor (monitor) { + requestAddMonitor (monitor) { this._monitorState[monitor.id] = monitor; } @@ -897,7 +882,7 @@ class Runtime extends EventEmitter { * exist in the state. * @param {!object} monitor Monitor to update. */ - updateMonitor (monitor) { + requestUpdateMonitor (monitor) { if (this._monitorState.hasOwnProperty(monitor.id)) { this._monitorState[monitor.id] = Object.assign({}, this._monitorState[monitor.id], monitor); } @@ -908,7 +893,7 @@ class Runtime extends EventEmitter { * not exist in the state. * @param {!object} monitorId ID of the monitor to remove. */ - removeMonitor (monitorId) { + requestRemoveMonitor (monitorId) { delete this._monitorState[monitorId]; } diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 241dcc9d..9afa43cd 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -58,12 +58,6 @@ class VirtualMachine extends EventEmitter { instance.runtime.on(Runtime.MONITORS_UPDATE, monitorList => { instance.emit(Runtime.MONITORS_UPDATE, monitorList); }); - instance.runtime.on(Runtime.MONITORS_REMOVED, monitorList => { - instance.emit(Runtime.MONITORS_REMOVED, monitorList); - }); - instance.runtime.on(Runtime.MONITORS_ADDED, monitorList => { - instance.emit(Runtime.MONITORS_ADDED, monitorList); - }); this.blockListener = this.blockListener.bind(this); this.flyoutBlockListener = this.flyoutBlockListener.bind(this); From 4cfedc8d9d8fd2d4e0ffbafdb29dabd47999856a Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 15 May 2017 10:18:48 -0400 Subject: [PATCH 344/721] fix merge conflicts --- src/engine/runtime.js | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 3ba62202..ff6a97b4 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -105,17 +105,10 @@ class Runtime extends EventEmitter { this._cloneCounter = 0; /** -<<<<<<< HEAD * List of all monitors. */ this._monitorState = {}; -======= - * Flag to emit a targets update at the end of a step. When target data - * changes, this flag is set to true. - * @type {boolean} - */ - this._refreshTargets = false; ->>>>>>> develop + /** * Whether the project is in "turbo mode." @@ -697,7 +690,7 @@ class Runtime extends EventEmitter { } if (this._refreshTargets) this.emit(Runtime.TARGETS_UPDATE); } - + /** * Get the number of threads in the given array that are monitor threads (threads * that update monitor values, and don't count as running a script). @@ -864,17 +857,6 @@ class Runtime extends EventEmitter { this.emit(Runtime.VISUAL_REPORT, {id: blockId, value: String(value)}); } - /** -<<<<<<< HEAD - * Emit a sprite info report if the provided target is the original sprite - * @param {!Target} target Target to report sprite info for. - */ - spriteInfoReport (target) { - if (!target.isOriginal) return; - - this.emit(Runtime.SPRITE_INFO_REPORT, target.toJSON()); - } - /** * Add a monitor to the state. If the monitor already exists in the state, * overwrites it. @@ -905,8 +887,6 @@ class Runtime extends EventEmitter { } /** -======= ->>>>>>> develop * Get a target by its id. * @param {string} targetId Id of target to find. * @return {?Target} The target, if found. From dcdb806b5756e0f67b0bab870e58c147357ab0ba Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 15 May 2017 10:45:20 -0400 Subject: [PATCH 345/721] fix merge more --- src/engine/runtime.js | 5 ++++- src/virtual-machine.js | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index ff6a97b4..16118043 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -688,7 +688,10 @@ class Runtime extends EventEmitter { // @todo: Only render when this.redrawRequested or clones rendered. this.renderer.draw(); } - if (this._refreshTargets) this.emit(Runtime.TARGETS_UPDATE); + // @todo only emit if monitors has changed since last time. + this.emit(Runtime.MONITORS_UPDATE, + Object.keys(this._monitorState).map(key => this._monitorState[key]) + ); } /** diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 01265e93..3bd41bbd 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -55,8 +55,8 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.TARGETS_UPDATE, () => { this.emitTargetsUpdate(); }); - instance.runtime.on(Runtime.MONITORS_UPDATE, monitorList => { - instance.emit(Runtime.MONITORS_UPDATE, monitorList); + this.runtime.on(Runtime.MONITORS_UPDATE, monitorList => { + this.emit(Runtime.MONITORS_UPDATE, monitorList); }); this.blockListener = this.blockListener.bind(this); From 65d998f473164eb362f58a91f84addf57bb78062 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 15 May 2017 10:47:36 -0400 Subject: [PATCH 346/721] attach issue to todo --- src/engine/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 16118043..f85a6fc6 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -688,7 +688,7 @@ class Runtime extends EventEmitter { // @todo: Only render when this.redrawRequested or clones rendered. this.renderer.draw(); } - // @todo only emit if monitors has changed since last time. + // @todo(vm#570) only emit if monitors has changed since last time. this.emit(Runtime.MONITORS_UPDATE, Object.keys(this._monitorState).map(key => this._monitorState[key]) ); From f1208720f6b19d76d884b2596e0396442d7b6d3f Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 15 May 2017 16:25:34 -0400 Subject: [PATCH 347/721] restore things lost in bad merge --- src/engine/runtime.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index f85a6fc6..7a460434 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -104,12 +104,18 @@ class Runtime extends EventEmitter { */ this._cloneCounter = 0; + /** + * Flag to emit a targets update at the end of a step. When target data + * changes, this flag is set to true. + * @type {boolean} + */ + this._refreshTargets = false; + /** * List of all monitors. */ this._monitorState = {}; - /** * Whether the project is in "turbo mode." * @type {Boolean} @@ -688,6 +694,8 @@ class Runtime extends EventEmitter { // @todo: Only render when this.redrawRequested or clones rendered. this.renderer.draw(); } + if (this._refreshTargets) this.emit(Runtime.TARGETS_UPDATE); + // @todo(vm#570) only emit if monitors has changed since last time. this.emit(Runtime.MONITORS_UPDATE, Object.keys(this._monitorState).map(key => this._monitorState[key]) From 548aa6dd70efd3ff89100cf30257b7e1b6f7e2bb Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Mon, 15 May 2017 21:11:38 -0700 Subject: [PATCH 348/721] Fix no-useless-call todo The linter is correct that `devObject[func].call(devObject, args)` is unnecessary because it's equivalent to `devObject[func](args)`. @tmickel probably either meant to allow passing an array of arguments to be applied, or to call the function with the provided argument. Since we probably want to be able to use multi-argument functions, use `apply` and fix the one place that uses `ioQuery` with an argument. --- src/blocks/scratch3_sensing.js | 2 +- src/engine/execute.js | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index e01f0cfd..98eeac97 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -114,7 +114,7 @@ class Scratch3SensingBlocks { } getKeyPressed (args, util) { - return util.ioQuery('keyboard', 'getKeyIsDown', args.KEY_OPTION); + return util.ioQuery('keyboard', 'getKeyIsDown', [args.KEY_OPTION]); } daysSince2000 () { diff --git a/src/engine/execute.js b/src/engine/execute.js index 2b66c5e7..01013089 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -202,12 +202,7 @@ const execute = function (sequencer, thread) { // Find the I/O device and execute the query/function call. if (runtime.ioDevices[device] && runtime.ioDevices[device][func]) { const devObject = runtime.ioDevices[device]; - // @todo Figure out why eslint complains about no-useless-call - // no-useless-call can't tell if the call is useless for dynamic - // expressions... or something. Not exactly sure why it - // complains here. - // eslint-disable-next-line no-useless-call - return devObject[func].call(devObject, args); + return devObject[func].apply(devObject, args); } } }); From 2c4361b8300ff271a8adb97ecf3667c712d519a1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 16 May 2017 09:20:52 -0400 Subject: [PATCH 349/721] Fix refreshTargets race condition --- src/engine/runtime.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 2708ebe7..c4ea2b35 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -645,7 +645,6 @@ class Runtime extends EventEmitter { * inactive threads after each iteration. */ _step () { - this._refreshTargets = false; // Find all edge-activated hats, and add them to threads to be evaluated. for (const hatType in this._hats) { if (!this._hats.hasOwnProperty(hatType)) continue; @@ -663,7 +662,10 @@ class Runtime extends EventEmitter { // @todo: Only render when this.redrawRequested or clones rendered. this.renderer.draw(); } - if (this._refreshTargets) this.emit(Runtime.TARGETS_UPDATE); + if (this._refreshTargets) { + this.emit(Runtime.TARGETS_UPDATE); + this._refreshTargets = false; + } } /** From cf188bc5e477e0b5c7b188bfee54bbaa80630a74 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 18 May 2017 02:07:35 +0900 Subject: [PATCH 350/721] Don't serialize clones when saving --- src/serialization/sb3.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 4477a7b5..161dbd2f 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -21,7 +21,7 @@ const loadSound = require('../import/load-sound.js'); const serialize = function (runtime) { // Fetch targets const obj = Object.create(null); - obj.targets = runtime.targets; + obj.targets = runtime.targets.filter(target => target.isOriginal); // Assemble metadata const meta = Object.create(null); From beaef901a72d075ded2e262bfb676db3ba058c6c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 18 May 2017 09:12:12 -0400 Subject: [PATCH 351/721] Add failing test for gui issues 370 Fix rename increment when renamed to itself --- src/virtual-machine.js | 3 +-- test/unit/virtual-machine.js | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 79c16d2f..adbeba64 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -329,9 +329,8 @@ class VirtualMachine extends EventEmitter { } if (newName && RESERVED_NAMES.indexOf(newName) === -1) { const names = this.runtime.targets - .filter(runtimeTarget => runtimeTarget.isSprite()) + .filter(runtimeTarget => runtimeTarget.isSprite() && runtimeTarget.id !== target.id) .map(runtimeTarget => runtimeTarget.sprite.name); - sprite.name = StringUtil.unusedName(newName, names); } this.emitTargetsUpdate(); diff --git a/test/unit/virtual-machine.js b/test/unit/virtual-machine.js index 73cefb8a..37a62ff6 100644 --- a/test/unit/virtual-machine.js +++ b/test/unit/virtual-machine.js @@ -94,3 +94,18 @@ test('renameSprite increments from existing sprite names', t => { t.equal(vm.runtime.targets[0].sprite.name, 'that name2'); t.end(); }); + +test('renameSprite does not increment when renaming to the same name', t => { + const vm = new VirtualMachine(); + vm.emitTargetsUpdate = () => {}; + vm.runtime.targets = [{ + id: 'id1', + isSprite: () => true, + sprite: { + name: 'this name' + } + }]; + vm.renameSprite('id1', 'this name'); + t.equal(vm.runtime.targets[0].sprite.name, 'this name'); + t.end(); +}); From 9645a513b1bbdf365e3b86fda136a31db67b0fb0 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 19 May 2017 12:28:05 -0400 Subject: [PATCH 352/721] Only send monitor update on change --- src/engine/runtime.js | 48 ++++++++++++++++++++++++++++++------- test/unit/engine_runtime.js | 46 +++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 9 deletions(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 92f98e43..bae3d0ba 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -2,6 +2,7 @@ const EventEmitter = require('events'); const Sequencer = require('./sequencer'); const Blocks = require('./blocks'); const Thread = require('./thread'); +const {Map} = require('immutable'); // Virtual I/O devices. const Clock = require('../io/clock'); @@ -114,7 +115,12 @@ class Runtime extends EventEmitter { /** * List of all monitors. */ - this._monitorState = {}; + this._monitorState = Map({}); + + /** + * Monitor state from last tick + */ + this._prevMonitorState = Map({}); /** * Whether the project is in "turbo mode." @@ -699,10 +705,10 @@ class Runtime extends EventEmitter { this._refreshTargets = false; } - // @todo(vm#570) only emit if monitors has changed since last time. - this.emit(Runtime.MONITORS_UPDATE, - Object.keys(this._monitorState).map(key => this._monitorState[key]) - ); + if (!this._prevMonitorState.equals(this._monitorState)) { + this.emit(Runtime.MONITORS_UPDATE, this._monitorState.toArray()); + } + this._prevMonitorState = this._monitorState; } /** @@ -877,7 +883,7 @@ class Runtime extends EventEmitter { * @param {!object} monitor Monitor to add. */ requestAddMonitor (monitor) { - this._monitorState[monitor.id] = monitor; + this._monitorState = this._monitorState.set(monitor.id, monitor); } /** @@ -886,18 +892,42 @@ class Runtime extends EventEmitter { * @param {!object} monitor Monitor to update. */ requestUpdateMonitor (monitor) { - if (this._monitorState.hasOwnProperty(monitor.id)) { - this._monitorState[monitor.id] = Object.assign({}, this._monitorState[monitor.id], monitor); + if (this._monitorState.has(monitor.id) && + this._monitorWouldChange(this._monitorState.get(monitor.id), monitor)) { + this._monitorState = + this._monitorState.set(monitor.id, Object.assign({}, this._monitorState.get(monitor.id), monitor)); } } + /** + * Whether or not the monitor would change when the new state is applied. New state has its properties + * applied to the current state, it does not completely overwrite the current state. For this to return + * accurately, the changes being applied should be on primitive types, otherwise it may false-positive. + * @param {!object} currentMonitorState the state of a monitor, the values in this._monitorState + * @param {!object} newMonitorDelta the state to be applied to a monitor. The IDs of current and new monitor + * should match. + * @return {boolean} whether the new state, when applied to the current state, would change the current state. + */ + _monitorWouldChange (currentMonitorState, newMonitorDelta) { + for (const prop in newMonitorDelta) { + if (currentMonitorState.hasOwnProperty(prop)) { + // Strict equals to check if properties change; switch this to deep equals if + // we expect monitors to have changing state that is not primative. + if (currentMonitorState[prop] !== newMonitorDelta[prop]) return true; + } else { + return true; // The new state would add a new property to the monitor + } + } + return false; + } + /** * Removes a monitor from the state. Does nothing if the monitor already does * not exist in the state. * @param {!object} monitorId ID of the monitor to remove. */ requestRemoveMonitor (monitorId) { - delete this._monitorState[monitorId]; + this._monitorState = this._monitorState.delete(monitorId); } /** diff --git a/test/unit/engine_runtime.js b/test/unit/engine_runtime.js index be431186..6fd04eef 100644 --- a/test/unit/engine_runtime.js +++ b/test/unit/engine_runtime.js @@ -10,3 +10,49 @@ test('spec', t => { t.end(); }); + +test('monitorWouldChange_false', t => { + const r = new Runtime(); + const currentMonitorState = { + id: 'xklj4#!', + category: 'data', + label: 'turtle whereabouts', + value: '25', + x: 0, + y: 0 + }; + const newMonitorDelta = { + id: 'xklj4#!', + value: String(25) + }; + t.equals(false, r._monitorWouldChange(currentMonitorState, newMonitorDelta)); + t.end(); +}); + +test('monitorWouldChange_true', t => { + const r = new Runtime(); + const currentMonitorState = { + id: 'xklj4#!', + category: 'data', + label: 'turtle whereabouts', + value: '25', + x: 0, + y: 0 + }; + + // Value change + let newMonitorDelta = { + id: 'xklj4#!', + value: String(24) + }; + t.equal(true, r._monitorWouldChange(currentMonitorState, newMonitorDelta)); + + // Prop change + newMonitorDelta = { + id: 'xklj4#!', + moose: 7 + }; + t.equal(true, r._monitorWouldChange(currentMonitorState, newMonitorDelta)); + + t.end(); +}); From 66c8d9550dfcc6c76ca790128940c93ded3307c7 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 19 May 2017 12:42:37 -0400 Subject: [PATCH 353/721] add dep to package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 0308aa6f..44bb99a9 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "gh-pages": "^0.12.0", "highlightjs": "^9.8.0", "htmlparser2": "3.9.2", + "immutable": "3.8.1", "json": "^9.0.4", "lodash.defaultsdeep": "4.6.0", "minilog": "3.1.0", From 48c51ab2ecc248d99632e0908ee84572c895cb7a Mon Sep 17 00:00:00 2001 From: DD Liu Date: Fri, 19 May 2017 17:28:00 -0400 Subject: [PATCH 354/721] switch to nested immutables state --- src/engine/blocks.js | 30 ++++++++++----------- src/engine/execute.js | 5 ++-- src/engine/runtime.js | 38 ++++++-------------------- test/unit/engine_runtime.js | 53 ++++++++++++++++++++++++------------- 4 files changed, 59 insertions(+), 67 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index ebe113f8..b26f6f52 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -1,6 +1,7 @@ const adapter = require('./adapter'); const mutationAdapter = require('./mutation-adapter'); const xmlEscape = require('../util/xml-escape'); +const {Map} = require('immutable'); /** * @fileoverview @@ -293,22 +294,19 @@ class Blocks { if (optRuntime && wasMonitored && !block.isMonitored) { optRuntime.requestRemoveMonitor(block.id); } else if (optRuntime && !wasMonitored && block.isMonitored) { - optRuntime.requestAddMonitor( - // Ensure that value is not undefined, since React requires it - { - // @todo(vm#564) this will collide if multiple sprites use same block - id: block.id, - category: 'data', - // @todo(vm#565) how to handle translation here? - label: block.opcode, - // @todo(vm#565) for numerical values with decimals, some countries use comma - value: '', - x: 0, - // @todo(vm#566) Don't require sending x and y when instantiating a - // monitor. If it's not preset the GUI should decide. - y: 0 - } - ); + optRuntime.requestAddMonitor(Map({ + // @todo(vm#564) this will collide if multiple sprites use same block + id: block.id, + category: 'data', + // @todo(vm#565) how to handle translation here? + label: block.opcode, + // @todo(vm#565) for numerical values with decimals, some countries use comma + value: '', // Ensure that value is not undefined, since React requires it + x: 0, + // @todo(vm#566) Don't require sending x and y when instantiating a + // monitor. If it's not preset the GUI should decide. + y: 0 + })); } break; } diff --git a/src/engine/execute.js b/src/engine/execute.js index 34e2e91b..3dfa7c1a 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -1,5 +1,6 @@ const log = require('../util/log'); const Thread = require('./thread'); +const {Map} = require('immutable'); /** * Utility function to determine if a value is a Promise. @@ -97,10 +98,10 @@ const execute = function (sequencer, thread) { runtime.visualReport(currentBlockId, resolvedValue); } if (thread.updateMonitor) { - runtime.requestUpdateMonitor({ + runtime.requestUpdateMonitor(Map({ id: currentBlockId, value: String(resolvedValue) - }); + })); } } // Finished any yields. diff --git a/src/engine/runtime.js b/src/engine/runtime.js index bae3d0ba..bdc6c29a 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -706,7 +706,8 @@ class Runtime extends EventEmitter { } if (!this._prevMonitorState.equals(this._monitorState)) { - this.emit(Runtime.MONITORS_UPDATE, this._monitorState.toArray()); + const monitorStateObj = this._monitorState.toJS(); + this.emit(Runtime.MONITORS_UPDATE, Object.keys(monitorStateObj).map(key => monitorStateObj[key])); } this._prevMonitorState = this._monitorState; } @@ -880,51 +881,28 @@ class Runtime extends EventEmitter { /** * Add a monitor to the state. If the monitor already exists in the state, * overwrites it. - * @param {!object} monitor Monitor to add. + * @param {!Map} monitor Monitor to add. */ requestAddMonitor (monitor) { - this._monitorState = this._monitorState.set(monitor.id, monitor); + this._monitorState = this._monitorState.set(monitor.get('id'), monitor); } /** * Update a monitor in the state. Does nothing if the monitor does not already * exist in the state. - * @param {!object} monitor Monitor to update. + * @param {!Map} monitor Monitor to update. */ requestUpdateMonitor (monitor) { - if (this._monitorState.has(monitor.id) && - this._monitorWouldChange(this._monitorState.get(monitor.id), monitor)) { + if (this._monitorState.has(monitor.get('id'))) { this._monitorState = - this._monitorState.set(monitor.id, Object.assign({}, this._monitorState.get(monitor.id), monitor)); + this._monitorState.set(monitor.get('id'), this._monitorState.get(monitor.get('id')).merge(monitor)); } } - /** - * Whether or not the monitor would change when the new state is applied. New state has its properties - * applied to the current state, it does not completely overwrite the current state. For this to return - * accurately, the changes being applied should be on primitive types, otherwise it may false-positive. - * @param {!object} currentMonitorState the state of a monitor, the values in this._monitorState - * @param {!object} newMonitorDelta the state to be applied to a monitor. The IDs of current and new monitor - * should match. - * @return {boolean} whether the new state, when applied to the current state, would change the current state. - */ - _monitorWouldChange (currentMonitorState, newMonitorDelta) { - for (const prop in newMonitorDelta) { - if (currentMonitorState.hasOwnProperty(prop)) { - // Strict equals to check if properties change; switch this to deep equals if - // we expect monitors to have changing state that is not primative. - if (currentMonitorState[prop] !== newMonitorDelta[prop]) return true; - } else { - return true; // The new state would add a new property to the monitor - } - } - return false; - } - /** * Removes a monitor from the state. Does nothing if the monitor already does * not exist in the state. - * @param {!object} monitorId ID of the monitor to remove. + * @param {!string} monitorId ID of the monitor to remove. */ requestRemoveMonitor (monitorId) { this._monitorState = this._monitorState.delete(monitorId); diff --git a/test/unit/engine_runtime.js b/test/unit/engine_runtime.js index 6fd04eef..dd60c5ed 100644 --- a/test/unit/engine_runtime.js +++ b/test/unit/engine_runtime.js @@ -1,5 +1,6 @@ const test = require('tap').test; const Runtime = require('../../src/engine/runtime'); +const {Map} = require('immutable'); test('spec', t => { const r = new Runtime(); @@ -11,48 +12,62 @@ test('spec', t => { t.end(); }); -test('monitorWouldChange_false', t => { +test('monitorStateEquals', t => { const r = new Runtime(); - const currentMonitorState = { - id: 'xklj4#!', + const id = 'xklj4#!'; + const prevMonitorState = Map({ + id, category: 'data', label: 'turtle whereabouts', value: '25', x: 0, y: 0 - }; - const newMonitorDelta = { - id: 'xklj4#!', + }); + const newMonitorDelta = Map({ + id, value: String(25) - }; - t.equals(false, r._monitorWouldChange(currentMonitorState, newMonitorDelta)); + }); + r.requestAddMonitor(prevMonitorState); + r.requestUpdateMonitor(newMonitorDelta); + + t.equals(true, prevMonitorState.equals(r._monitorState.get(id))); + t.equals(String(25), r._monitorState.get(id).get('value')); t.end(); }); -test('monitorWouldChange_true', t => { +test('monitorStateDoesNotEqual', t => { const r = new Runtime(); - const currentMonitorState = { - id: 'xklj4#!', + const id = 'xklj4#!'; + const prevMonitorState = Map({ + id, category: 'data', label: 'turtle whereabouts', value: '25', x: 0, y: 0 - }; + }); // Value change - let newMonitorDelta = { - id: 'xklj4#!', + let newMonitorDelta = Map({ + id, value: String(24) - }; - t.equal(true, r._monitorWouldChange(currentMonitorState, newMonitorDelta)); + }); + r.requestAddMonitor(prevMonitorState); + r.requestUpdateMonitor(newMonitorDelta); + + t.equals(false, prevMonitorState.equals(r._monitorState.get(id))); + t.equals(String(24), r._monitorState.get(id).get('value')); // Prop change - newMonitorDelta = { + newMonitorDelta = Map({ id: 'xklj4#!', moose: 7 - }; - t.equal(true, r._monitorWouldChange(currentMonitorState, newMonitorDelta)); + }); + r.requestUpdateMonitor(newMonitorDelta); + + t.equals(false, prevMonitorState.equals(r._monitorState.get(id))); + t.equals(String(24), r._monitorState.get(id).get('value')); + t.equals(7, r._monitorState.get(id).get('moose')); t.end(); }); From 5b81c8b087153dc6b32c1bb31b1a672fecb9b400 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sun, 21 May 2017 12:26:16 -0400 Subject: [PATCH 355/721] Update sb2_specmap.js --- src/serialization/sb2_specmap.js | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/serialization/sb2_specmap.js b/src/serialization/sb2_specmap.js index a16104f6..0f62a42f 100644 --- a/src/serialization/sb2_specmap.js +++ b/src/serialization/sb2_specmap.js @@ -166,9 +166,8 @@ const specMap = { opcode: 'motion_setrotationstyle', argMap: [ { - type: 'input', - inputOp: 'motion_setrotationstyle_menu', - inputName: 'STYLE' + type: 'field', + fieldName: 'STYLE' } ] }, @@ -276,9 +275,8 @@ const specMap = { opcode: 'looks_changeeffectby', argMap: [ { - type: 'input', - inputOp: 'looks_effectmenu', - inputName: 'EFFECT' + type: 'field', + fieldName: 'EFFECT' }, { type: 'input', @@ -291,9 +289,8 @@ const specMap = { opcode: 'looks_seteffectto', argMap: [ { - type: 'input', - inputOp: 'looks_effectmenu', - inputName: 'EFFECT' + type: 'field', + fieldName: 'EFFECT' }, { type: 'input', @@ -1171,9 +1168,8 @@ const specMap = { opcode: 'operator_mathop', argMap: [ { - type: 'input', - inputOp: 'operator_mathop_menu', - inputName: 'OPERATOR' + type: 'field', + fieldName: 'OPERATOR' }, { type: 'input', @@ -1186,9 +1182,8 @@ const specMap = { opcode: 'data_variable', argMap: [ { - type: 'input', - inputOp: 'data_variablemenu', - inputName: 'VARIABLE' + type: 'field', + fieldName: 'VARIABLE' } ] }, From 73cb82319907b568febc5b74c5c91346e0506329 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 15 May 2017 08:30:02 -0400 Subject: [PATCH 356/721] Add delete costume and sound functions to the vm public api --- src/virtual-machine.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index cf4d0e63..a0da9b22 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -286,6 +286,20 @@ class VirtualMachine extends EventEmitter { }); } + /** + * Delete a costume from the current editing target. + * @param {int} costumeIndex - the index of the costume to be removed. + */ + deleteCostume (costumeIndex) { + this.editingTarget.sprite.costumes = this.editingTarget.sprite.costumes + .slice(0, costumeIndex) + .concat(this.editingTarget.sprite.costumes.slice(costumeIndex + 1)); + if (costumeIndex === this.editingTarget.currentCostume) { + this.editingTarget.setCostume(costumeIndex - 1); + } + this.emitTargetsUpdate(); + } + /** * Add a sound to the current editing target. * @param {!object} soundObject Object representing the costume. @@ -298,6 +312,17 @@ class VirtualMachine extends EventEmitter { }); } + /** + * Delete a sound from the current editing target. + * @param {int} soundIndex - the index of the sound to be removed. + */ + deleteSound (soundIndex) { + this.editingTarget.sprite.sounds = this.editingTarget.sprite.sounds + .slice(0, soundIndex) + .concat(this.editingTarget.sprite.sounds.slice(soundIndex + 1)); + this.emitTargetsUpdate(); + } + /** * Add a backdrop to the stage. * @param {string} md5ext - the MD5 and extension of the backdrop to be loaded. From 71081502fe4be6fd8abdb7caadab8eeeba984d5c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 15 May 2017 08:32:55 -0400 Subject: [PATCH 357/721] Add to the vm spec --- test/unit/spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/unit/spec.js b/test/unit/spec.js index 7e211556..48b0fd6a 100644 --- a/test/unit/spec.js +++ b/test/unit/spec.js @@ -19,6 +19,8 @@ test('interface', t => { t.type(vm.addCostume, 'function'); t.type(vm.addBackdrop, 'function'); t.type(vm.addSound, 'function'); + t.type(vm.deleteCostume, 'function'); + t.type(vm.deleteSound, 'function'); t.type(vm.renameSprite, 'function'); t.type(vm.deleteSprite, 'function'); From a608ca4fed76e3b3f2382a22ba957bc349d9f226 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 16 May 2017 11:01:50 -0400 Subject: [PATCH 358/721] Make rendered targets in charge of deleting their own sounds and costumes --- src/sprites/rendered-target.js | 33 ++++++++++++++ src/virtual-machine.js | 13 +----- test/unit/sprites_rendered-target.js | 65 ++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 11 deletions(-) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 8489b37c..752debaf 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -389,6 +389,39 @@ class RenderedTarget extends Target { this.runtime.requestTargetsUpdate(this); } + /** + * Delete a costume by index. + * @param {number} index Costume index to be deleted + */ + deleteCostume (index) { + const originalCostumeCount = this.sprite.costumes.length; + if (originalCostumeCount === 1) return; + + this.sprite.costumes = this.sprite.costumes + .slice(0, index) + .concat(this.sprite.costumes.slice(index + 1)); + + if (index === originalCostumeCount - 1) { + this.setCostume(index - 1); + } else { + this.setCostume(index); + } + + this.runtime.requestTargetsUpdate(this); + } + + /** + * Delete a sound by index. + * @param {number} index Sound index to be deleted + */ + deleteSound (index) { + if (this.sprite.sounds.length === 1) return; + this.sprite.sounds = this.sprite.sounds + .slice(0, index) + .concat(this.sprite.sounds.slice(index + 1)); + this.runtime.requestTargetsUpdate(this); + } + /** * Update the rotation style. * @param {!string} rotationStyle New rotation style. diff --git a/src/virtual-machine.js b/src/virtual-machine.js index a0da9b22..fef841a6 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -291,13 +291,7 @@ class VirtualMachine extends EventEmitter { * @param {int} costumeIndex - the index of the costume to be removed. */ deleteCostume (costumeIndex) { - this.editingTarget.sprite.costumes = this.editingTarget.sprite.costumes - .slice(0, costumeIndex) - .concat(this.editingTarget.sprite.costumes.slice(costumeIndex + 1)); - if (costumeIndex === this.editingTarget.currentCostume) { - this.editingTarget.setCostume(costumeIndex - 1); - } - this.emitTargetsUpdate(); + this.editingTarget.deleteCostume(costumeIndex); } /** @@ -317,10 +311,7 @@ class VirtualMachine extends EventEmitter { * @param {int} soundIndex - the index of the sound to be removed. */ deleteSound (soundIndex) { - this.editingTarget.sprite.sounds = this.editingTarget.sprite.sounds - .slice(0, soundIndex) - .concat(this.editingTarget.sprite.sounds.slice(soundIndex + 1)); - this.emitTargetsUpdate(); + this.editingTarget.deleteSound(soundIndex); } /** diff --git a/test/unit/sprites_rendered-target.js b/test/unit/sprites_rendered-target.js index 3f8ee2c3..f564bfd3 100644 --- a/test/unit/sprites_rendered-target.js +++ b/test/unit/sprites_rendered-target.js @@ -98,6 +98,71 @@ test('setCostume', t => { t.end(); }); +test('deleteCostume', t => { + const o1 = {id: 1}; + const o2 = {id: 2}; + const o3 = {id: 3}; + + const s = new Sprite(); + const r = new Runtime(); + s.costumes = [o1, o2, o3]; + const a = new RenderedTarget(s, r); + const renderer = new FakeRenderer(); + a.renderer = renderer; + + // Deleting costume keeps costume index at 0 + a.setCostume(0); + a.deleteCostume(0); + t.deepEqual(a.sprite.costumes, [o2, o3]); + t.equals(a.currentCostume, 0); + + // Deleting a costume in the middle maintains current costume index + a.sprite.costumes = [o1, o2, o3]; + a.setCostume(1); + a.deleteCostume(1); + t.deepEqual(a.sprite.costumes, [o1, o3]); + t.equals(a.currentCostume, 1); + + // Deleting last costume selects previous costume + a.sprite.costumes = [o1, o2, o3]; + a.setCostume(2); + a.deleteCostume(2); + t.deepEqual(a.sprite.costumes, [o1, o2]); + t.equals(a.currentCostume, 1); + + // Refuses to delete only costume + a.sprite.costumes = [o1]; + a.setCostume(0); + a.deleteCostume(0); + t.deepEqual(a.sprite.costumes, [o1]); + t.equals(a.currentCostume, 0); + + t.end(); +}); + +test('deleteSound', t => { + const o1 = {id: 1}; + const o2 = {id: 2}; + const o3 = {id: 3}; + + const s = new Sprite(); + const r = new Runtime(); + s.sounds = [o1, o2, o3]; + const a = new RenderedTarget(s, r); + const renderer = new FakeRenderer(); + a.renderer = renderer; + + a.deleteSound(0); + t.deepEqual(a.sprite.sounds, [o2, o3]); + + // Refuses to delete only sound + a.sprite.sounds = [o1]; + a.deleteSound(0); + t.deepEqual(a.sprite.sounds, [o1]); + + t.end(); +}); + test('setRotationStyle', t => { const s = new Sprite(); const r = new Runtime(); From 30a0c0d251a917166ff2f6d7bd6e55128daa5112 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 18 May 2017 13:29:44 -0400 Subject: [PATCH 359/721] Allow deleting the last sound --- src/sprites/rendered-target.js | 1 - test/unit/sprites_rendered-target.js | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 752debaf..1e7ff9a2 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -415,7 +415,6 @@ class RenderedTarget extends Target { * @param {number} index Sound index to be deleted */ deleteSound (index) { - if (this.sprite.sounds.length === 1) return; this.sprite.sounds = this.sprite.sounds .slice(0, index) .concat(this.sprite.sounds.slice(index + 1)); diff --git a/test/unit/sprites_rendered-target.js b/test/unit/sprites_rendered-target.js index f564bfd3..5e7f881b 100644 --- a/test/unit/sprites_rendered-target.js +++ b/test/unit/sprites_rendered-target.js @@ -155,10 +155,10 @@ test('deleteSound', t => { a.deleteSound(0); t.deepEqual(a.sprite.sounds, [o2, o3]); - // Refuses to delete only sound + // Allows deleting the only sound a.sprite.sounds = [o1]; a.deleteSound(0); - t.deepEqual(a.sprite.sounds, [o1]); + t.deepEqual(a.sprite.sounds, []); t.end(); }); From 423da12c3c2a176d2d7573d147c32f8f59b86bf4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 18 May 2017 13:56:14 -0400 Subject: [PATCH 360/721] Handle more cases of deleting costumes --- src/sprites/rendered-target.js | 6 ++-- test/unit/sprites_rendered-target.js | 46 ++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 1e7ff9a2..06528ece 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -401,10 +401,10 @@ class RenderedTarget extends Target { .slice(0, index) .concat(this.sprite.costumes.slice(index + 1)); - if (index === originalCostumeCount - 1) { + if (index === this.currentCostume && index === originalCostumeCount - 1) { this.setCostume(index - 1); - } else { - this.setCostume(index); + } else if (index < this.currentCostume) { + this.setCostume(this.currentCostume - 1); } this.runtime.requestTargetsUpdate(this); diff --git a/test/unit/sprites_rendered-target.js b/test/unit/sprites_rendered-target.js index 5e7f881b..ecb4a089 100644 --- a/test/unit/sprites_rendered-target.js +++ b/test/unit/sprites_rendered-target.js @@ -102,6 +102,8 @@ test('deleteCostume', t => { const o1 = {id: 1}; const o2 = {id: 2}; const o3 = {id: 3}; + const o4 = {id: 4}; + const o5 = {id: 5}; const s = new Sprite(); const r = new Runtime(); @@ -110,20 +112,26 @@ test('deleteCostume', t => { const renderer = new FakeRenderer(); a.renderer = renderer; - // Deleting costume keeps costume index at 0 + // x* Costume 1 * Costume 2 + // Costume 2 => Costume 3 + // Costume 3 a.setCostume(0); a.deleteCostume(0); t.deepEqual(a.sprite.costumes, [o2, o3]); t.equals(a.currentCostume, 0); - // Deleting a costume in the middle maintains current costume index + // Costume 1 Costume 1 + // x* Costume 2 => * Costume 3 + // Costume 3 a.sprite.costumes = [o1, o2, o3]; a.setCostume(1); a.deleteCostume(1); t.deepEqual(a.sprite.costumes, [o1, o3]); t.equals(a.currentCostume, 1); - // Deleting last costume selects previous costume + // Costume 1 Costume 1 + // Costume 2 => * Costume 2 + // x* Costume 3 a.sprite.costumes = [o1, o2, o3]; a.setCostume(2); a.deleteCostume(2); @@ -137,6 +145,38 @@ test('deleteCostume', t => { t.deepEqual(a.sprite.costumes, [o1]); t.equals(a.currentCostume, 0); + // Costume 1 Costume 1 + // x Costume 2 Costume 3 + // Costume 3 => * Costume 4 + // * Costume 4 Costume 5 + // Costume 5 + a.sprite.costumes = [o1, o2, o3, o4, o5]; + a.setCostume(3); + a.deleteCostume(1); + t.deepEqual(a.sprite.costumes, [o1, o3, o4, o5]); + t.equals(a.currentCostume, 2); + + // Costume 1 Costume 1 + // * Costume 2 * Costume 2 + // Costume 3 => Costume 3 + // x Costume 4 Costume 5 + // Costume 5 + a.sprite.costumes = [o1, o2, o3, o4, o5]; + a.setCostume(1); + a.deleteCostume(3); + t.deepEqual(a.sprite.costumes, [o1, o2, o3, o5]); + t.equals(a.currentCostume, 1); + + // Costume 1 Costume 1 + // * Costume 2 * Costume 2 + // Costume 3 => Costume 3 + // Costume 4 Costume 4 + // x Costume 5 + a.sprite.costumes = [o1, o2, o3, o4, o5]; + a.setCostume(1); + a.deleteCostume(4); + t.deepEqual(a.sprite.costumes, [o1, o2, o3, o4]); + t.equals(a.currentCostume, 1); t.end(); }); From 3eaec99e51fd87bad4dba2dfdeb99b293666bf3e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 22 May 2017 08:14:13 -0400 Subject: [PATCH 361/721] Always activate the current costume after deleting --- src/sprites/rendered-target.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 06528ece..0bc5cff6 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -405,6 +405,8 @@ class RenderedTarget extends Target { this.setCostume(index - 1); } else if (index < this.currentCostume) { this.setCostume(this.currentCostume - 1); + } else { + this.setCostume(this.currentCostume); } this.runtime.requestTargetsUpdate(this); From 144a0b349df8cc46bdab79de312d2bf2d5e819b5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 22 May 2017 13:38:29 -0400 Subject: [PATCH 362/721] Change monitor reporting from category and label to opcode --- src/engine/blocks.js | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index ebe113f8..6177e400 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -278,7 +278,7 @@ class Blocks { const block = this._blocks[args.id]; if (typeof block === 'undefined') return; - let wasMonitored = block.isMonitored; + const wasMonitored = block.isMonitored; switch (args.element) { case 'field': // Update block value @@ -293,22 +293,17 @@ class Blocks { if (optRuntime && wasMonitored && !block.isMonitored) { optRuntime.requestRemoveMonitor(block.id); } else if (optRuntime && !wasMonitored && block.isMonitored) { - optRuntime.requestAddMonitor( - // Ensure that value is not undefined, since React requires it - { - // @todo(vm#564) this will collide if multiple sprites use same block - id: block.id, - category: 'data', - // @todo(vm#565) how to handle translation here? - label: block.opcode, - // @todo(vm#565) for numerical values with decimals, some countries use comma - value: '', - x: 0, - // @todo(vm#566) Don't require sending x and y when instantiating a - // monitor. If it's not preset the GUI should decide. - y: 0 - } - ); + optRuntime.requestAddMonitor({ + // @todo(vm#564) this will collide if multiple sprites use same block + id: block.id, + opcode: block.opcode, + // @todo(vm#565) for numerical values with decimals, some countries use comma + value: '', + // @todo(vm#566) Don't require sending x and y when instantiating a + // monitor. If it's not preset the GUI should decide. + x: 0, + y: 0 + }); } break; } @@ -370,7 +365,7 @@ class Blocks { } } - + /** * Block management: run all blocks. * @param {!object} runtime Runtime to run all blocks in. From 817ea47e3261c87195a52ce4a1ffe8b60c600eb6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 22 May 2017 14:40:07 -0400 Subject: [PATCH 363/721] Remove XY from monitor reporting --- src/engine/blocks.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 6177e400..a65e4ac1 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -298,11 +298,7 @@ class Blocks { id: block.id, opcode: block.opcode, // @todo(vm#565) for numerical values with decimals, some countries use comma - value: '', - // @todo(vm#566) Don't require sending x and y when instantiating a - // monitor. If it's not preset the GUI should decide. - x: 0, - y: 0 + value: '' }); } break; From 5973e2d305bdf3762001bee94de4326c77572ede Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 22 May 2017 15:18:48 -0400 Subject: [PATCH 364/721] Add reporting for block params --- src/engine/blocks.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index a65e4ac1..c0595171 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -267,6 +267,25 @@ class Blocks { } } + /** + * Serialize block fields and input fields for reporting new monitors + * @param {!object} block Block to be paramified. + * @return {!object} object of param key/values. + */ + getBlockParams (block) { + const params = {}; + for (const key in block.fields) { + params[key] = block.fields[key].value; + } + for (const inputKey in block.inputs) { + const inputBlock = this._blocks[block.inputs[inputKey].block]; + for (const key in inputBlock.fields) { + params[key] = inputBlock.fields[key].value; + } + } + return params; + } + /** * Block management: change block field values * @param {!object} args Blockly change event to be processed @@ -297,6 +316,7 @@ class Blocks { // @todo(vm#564) this will collide if multiple sprites use same block id: block.id, opcode: block.opcode, + params: this.getBlockParams(block), // @todo(vm#565) for numerical values with decimals, some countries use comma value: '' }); From acdc783f27aa3e7fda756a5e04d1c4758cf61129 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 22 May 2017 17:31:53 -0400 Subject: [PATCH 365/721] emit immutable --- src/engine/runtime.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index bdc6c29a..8ecbb6b1 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -706,10 +706,9 @@ class Runtime extends EventEmitter { } if (!this._prevMonitorState.equals(this._monitorState)) { - const monitorStateObj = this._monitorState.toJS(); - this.emit(Runtime.MONITORS_UPDATE, Object.keys(monitorStateObj).map(key => monitorStateObj[key])); + this.emit(Runtime.MONITORS_UPDATE, this._monitorState); + this._prevMonitorState = this._monitorState; } - this._prevMonitorState = this._monitorState; } /** From df9230a0ff98317ea3303dd437d906b1ec907b8d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 23 May 2017 09:55:42 -0400 Subject: [PATCH 366/721] Make helper private --- src/engine/blocks.js | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index c0595171..8a3436f5 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -267,25 +267,6 @@ class Blocks { } } - /** - * Serialize block fields and input fields for reporting new monitors - * @param {!object} block Block to be paramified. - * @return {!object} object of param key/values. - */ - getBlockParams (block) { - const params = {}; - for (const key in block.fields) { - params[key] = block.fields[key].value; - } - for (const inputKey in block.inputs) { - const inputBlock = this._blocks[block.inputs[inputKey].block]; - for (const key in inputBlock.fields) { - params[key] = inputBlock.fields[key].value; - } - } - return params; - } - /** * Block management: change block field values * @param {!object} args Blockly change event to be processed @@ -316,7 +297,7 @@ class Blocks { // @todo(vm#564) this will collide if multiple sprites use same block id: block.id, opcode: block.opcode, - params: this.getBlockParams(block), + params: this._getBlockParams(block), // @todo(vm#565) for numerical values with decimals, some countries use comma value: '' }); @@ -525,6 +506,24 @@ class Blocks { } // --------------------------------------------------------------------- + /** + * Helper to serialize block fields and input fields for reporting new monitors + * @param {!object} block Block to be paramified. + * @return {!object} object of param key/values. + */ + _getBlockParams (block) { + const params = {}; + for (const key in block.fields) { + params[key] = block.fields[key].value; + } + for (const inputKey in block.inputs) { + const inputBlock = this._blocks[block.inputs[inputKey].block]; + for (const key in inputBlock.fields) { + params[key] = inputBlock.fields[key].value; + } + } + return params; + } /** * Helper to add a stack to `this._scripts`. From 1902848ea4e67683bc4371ede0e54c6e223041ac Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 24 May 2017 15:42:29 -0400 Subject: [PATCH 367/721] Switch to ordered maps of monitor records for monitor state --- src/engine/blocks.js | 4 ++-- src/engine/runtime.js | 14 ++++++++------ test/unit/engine_runtime.js | 13 ++++++------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index b26f6f52..19921f83 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -1,7 +1,7 @@ const adapter = require('./adapter'); const mutationAdapter = require('./mutation-adapter'); const xmlEscape = require('../util/xml-escape'); -const {Map} = require('immutable'); +const MonitorRecord = require('./records'); /** * @fileoverview @@ -294,7 +294,7 @@ class Blocks { if (optRuntime && wasMonitored && !block.isMonitored) { optRuntime.requestRemoveMonitor(block.id); } else if (optRuntime && !wasMonitored && block.isMonitored) { - optRuntime.requestAddMonitor(Map({ + optRuntime.requestAddMonitor(MonitorRecord({ // @todo(vm#564) this will collide if multiple sprites use same block id: block.id, category: 'data', diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 8ecbb6b1..26dc4d35 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -2,7 +2,7 @@ const EventEmitter = require('events'); const Sequencer = require('./sequencer'); const Blocks = require('./blocks'); const Thread = require('./thread'); -const {Map} = require('immutable'); +const {OrderedMap} = require('immutable'); // Virtual I/O devices. const Clock = require('../io/clock'); @@ -113,14 +113,14 @@ class Runtime extends EventEmitter { this._refreshTargets = false; /** - * List of all monitors. + * Ordered map of all monitors, which are MonitorReporter objects. */ - this._monitorState = Map({}); + this._monitorState = OrderedMap({}); /** * Monitor state from last tick */ - this._prevMonitorState = Map({}); + this._prevMonitorState = OrderedMap({}); /** * Whether the project is in "turbo mode." @@ -880,7 +880,7 @@ class Runtime extends EventEmitter { /** * Add a monitor to the state. If the monitor already exists in the state, * overwrites it. - * @param {!Map} monitor Monitor to add. + * @param {!MonitorRecord} monitor Monitor to add. */ requestAddMonitor (monitor) { this._monitorState = this._monitorState.set(monitor.get('id'), monitor); @@ -889,7 +889,9 @@ class Runtime extends EventEmitter { /** * Update a monitor in the state. Does nothing if the monitor does not already * exist in the state. - * @param {!Map} monitor Monitor to update. + * @param {!Map} monitor Monitor values to update. Values on the monitor with overwrite + * values on the old monitor with the same ID. If a value isn't defined on the new monitor, + * the old monitor will keep its old value. */ requestUpdateMonitor (monitor) { if (this._monitorState.has(monitor.get('id'))) { diff --git a/test/unit/engine_runtime.js b/test/unit/engine_runtime.js index dd60c5ed..8087bab6 100644 --- a/test/unit/engine_runtime.js +++ b/test/unit/engine_runtime.js @@ -1,5 +1,6 @@ const test = require('tap').test; const Runtime = require('../../src/engine/runtime'); +const MonitorRecord = require('../../src/engine/records'); const {Map} = require('immutable'); test('spec', t => { @@ -15,7 +16,7 @@ test('spec', t => { test('monitorStateEquals', t => { const r = new Runtime(); const id = 'xklj4#!'; - const prevMonitorState = Map({ + const prevMonitorState = MonitorRecord({ id, category: 'data', label: 'turtle whereabouts', @@ -38,13 +39,11 @@ test('monitorStateEquals', t => { test('monitorStateDoesNotEqual', t => { const r = new Runtime(); const id = 'xklj4#!'; - const prevMonitorState = Map({ + const prevMonitorState = MonitorRecord({ id, category: 'data', label: 'turtle whereabouts', - value: '25', - x: 0, - y: 0 + value: '25' }); // Value change @@ -61,13 +60,13 @@ test('monitorStateDoesNotEqual', t => { // Prop change newMonitorDelta = Map({ id: 'xklj4#!', - moose: 7 + x: 7 }); r.requestUpdateMonitor(newMonitorDelta); t.equals(false, prevMonitorState.equals(r._monitorState.get(id))); t.equals(String(24), r._monitorState.get(id).get('value')); - t.equals(7, r._monitorState.get(id).get('moose')); + t.equals(7, r._monitorState.get(id).get('x')); t.end(); }); From 2867b5c331daeb9c16a59aa2205e38b5aa773ba2 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 24 May 2017 15:43:12 -0400 Subject: [PATCH 368/721] add record file --- src/engine/records.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/engine/records.js diff --git a/src/engine/records.js b/src/engine/records.js new file mode 100644 index 00000000..39f927f8 --- /dev/null +++ b/src/engine/records.js @@ -0,0 +1,12 @@ +const {Record} = require('immutable'); + +const MonitorRecord = Record({ + id: null, + category: 'data', + label: null, + value: null, + x: null, + y: null +}); + +module.exports = MonitorRecord; From 18282fc1d0925b91334138c70609506f78aafc36 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 24 May 2017 15:48:11 -0400 Subject: [PATCH 369/721] remove get for records --- src/engine/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 26dc4d35..495dd169 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -883,7 +883,7 @@ class Runtime extends EventEmitter { * @param {!MonitorRecord} monitor Monitor to add. */ requestAddMonitor (monitor) { - this._monitorState = this._monitorState.set(monitor.get('id'), monitor); + this._monitorState = this._monitorState.set(monitor.id, monitor); } /** From eb931fd99bf0a32e694e2ac67d495ddd7799ebfd Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Wed, 24 May 2017 15:30:29 -0700 Subject: [PATCH 370/721] Specify dataFormat when loading asset for import When importing a project we know the file extension for each asset to be loaded. This change provides that information to the storage system so that we can load assets which don't use the default. For example, this allows loading JPG-format backdrops. In support of this change, there's a new function on `StringUtil` called `splitFirst`, which splits a string on the first instance of a separator character. This change includes unit tests for this new function. --- src/import/load-costume.js | 9 +++++---- src/import/load-sound.js | 6 ++++-- src/playground/playground.js | 2 +- src/serialization/sb3.js | 2 +- src/util/string-util.js | 24 ++++++++++++++++++++++++ test/fixtures/attach-test-storage.js | 2 +- test/unit/util_string.js | 8 ++++++++ 7 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/import/load-costume.js b/src/import/load-costume.js index a9323398..54962ea1 100644 --- a/src/import/load-costume.js +++ b/src/import/load-costume.js @@ -1,3 +1,4 @@ +const StringUtil = require('../util/string-util'); const log = require('../util/log'); /** @@ -19,17 +20,17 @@ const loadCostume = function (md5ext, costume, runtime) { } const AssetType = runtime.storage.AssetType; - const idParts = md5ext.split('.'); + const idParts = StringUtil.splitFirst(md5ext, '.'); const md5 = idParts[0]; - const ext = idParts[1].toUpperCase(); - const assetType = (ext === 'SVG') ? AssetType.ImageVector : AssetType.ImageBitmap; + const ext = idParts[1].toLowerCase(); + const assetType = (ext === 'svg') ? AssetType.ImageVector : AssetType.ImageBitmap; const rotationCenter = [ costume.rotationCenterX / costume.bitmapResolution, costume.rotationCenterY / costume.bitmapResolution ]; - let promise = runtime.storage.load(assetType, md5).then(costumeAsset => { + let promise = runtime.storage.load(assetType, md5, ext).then(costumeAsset => { costume.assetId = costumeAsset.assetId; costume.assetType = assetType; return costumeAsset; diff --git a/src/import/load-sound.js b/src/import/load-sound.js index a308e151..80b59598 100644 --- a/src/import/load-sound.js +++ b/src/import/load-sound.js @@ -1,3 +1,4 @@ +const StringUtil = require('../util/string-util'); const log = require('../util/log'); /** @@ -17,9 +18,10 @@ const loadSound = function (sound, runtime) { log.error('No audio engine present; cannot load sound asset: ', sound.md5); return Promise.resolve(sound); } - const idParts = sound.md5.split('.'); + const idParts = StringUtil.splitFirst(sound.md5, '.'); const md5 = idParts[0]; - return runtime.storage.load(runtime.storage.AssetType.Sound, md5) + const ext = idParts[1].toLowerCase(); + return runtime.storage.load(runtime.storage.AssetType.Sound, md5, ext) .then(soundAsset => { sound.assetId = soundAsset.assetId; sound.assetType = runtime.storage.AssetType.Sound; diff --git a/src/playground/playground.js b/src/playground/playground.js index c1b655a5..814bfb62 100644 --- a/src/playground/playground.js +++ b/src/playground/playground.js @@ -34,7 +34,7 @@ const getAssetUrl = function (asset) { 'internalapi/asset/', asset.assetId, '.', - asset.assetType.runtimeFormat, + asset.dataFormat, '/get/' ]; return assetUrlParts.join(''); diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 4477a7b5..9708b919 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -75,7 +75,7 @@ const parseScratchObject = function (object, runtime) { rotationCenterX: costumeSource.rotationCenterX, rotationCenterY: costumeSource.rotationCenterY }; - const costumeMd5 = `${costumeSource.assetId}.${costumeSource.assetType.runtimeFormat}`; + const costumeMd5 = `${costumeSource.assetId}.${costumeSource.dataFormat}`; return loadCostume(costumeMd5, costume, runtime); }); // Sounds from JSON diff --git a/src/util/string-util.js b/src/util/string-util.js index 0ee71254..48dde027 100644 --- a/src/util/string-util.js +++ b/src/util/string-util.js @@ -12,6 +12,30 @@ class StringUtil { while (existingNames.indexOf(name + i) >= 0) i++; return name + i; } + + /** + * Split a string on the first occurrence of a split character. + * @param {string} text - the string to split. + * @param {string} separator - split the text on this character. + * @returns {[string, string]} - the two parts of the split string, or [text, null] if no split character found. + * @example + * // returns ['foo', 'tar.gz'] + * splitFirst('foo.tar.gz', '.'); + * @example + * // returns ['foo', null] + * splitFirst('foo', '.'); + * @example + * // returns ['foo', ''] + * splitFirst('foo.', '.'); + */ + static splitFirst (text, separator) { + const index = text.indexOf(separator); + if (index >= 0) { + return [text.substring(0, index), text.substring(index + 1)]; + } else { + return [text, null]; + } + } } module.exports = StringUtil; diff --git a/test/fixtures/attach-test-storage.js b/test/fixtures/attach-test-storage.js index 3e75f596..bb0cc3a0 100644 --- a/test/fixtures/attach-test-storage.js +++ b/test/fixtures/attach-test-storage.js @@ -26,7 +26,7 @@ const getAssetUrl = function (asset) { 'internalapi/asset/', asset.assetId, '.', - asset.assetType.runtimeFormat, + asset.dataFormat, '/get/' ]; return assetUrlParts.join(''); diff --git a/test/unit/util_string.js b/test/unit/util_string.js index 74133625..aca20f9a 100644 --- a/test/unit/util_string.js +++ b/test/unit/util_string.js @@ -1,6 +1,14 @@ const test = require('tap').test; const StringUtil = require('../../src/util/string-util'); +test('splitFirst', t => { + t.deepEqual(StringUtil.splitFirst('asdf.1234', '.'), ['asdf', '1234']); + t.deepEqual(StringUtil.splitFirst('asdf.', '.'), ['asdf', '']); + t.deepEqual(StringUtil.splitFirst('.1234', '.'), ['', '1234']); + t.deepEqual(StringUtil.splitFirst('foo', '.'), ['foo', null]); + t.end(); +}); + test('withoutTrailingDigits', t => { t.strictEqual(StringUtil.withoutTrailingDigits('boeing747'), 'boeing'); t.strictEqual(StringUtil.withoutTrailingDigits('boeing747 '), 'boeing747 '); From 2625529ebe2a921eb3f912b69f96948a616a83ea Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Wed, 3 May 2017 16:39:31 -0700 Subject: [PATCH 371/721] Create WeDo 2.0 device communication classes --- src/blocks/scratch3_wedo2.js | 387 +++++++++++++++++++++++++++++++++++ src/engine/runtime.js | 3 +- 2 files changed, 389 insertions(+), 1 deletion(-) create mode 100644 src/blocks/scratch3_wedo2.js diff --git a/src/blocks/scratch3_wedo2.js b/src/blocks/scratch3_wedo2.js new file mode 100644 index 00000000..ac945ef8 --- /dev/null +++ b/src/blocks/scratch3_wedo2.js @@ -0,0 +1,387 @@ +const log = require('../util/log'); + +/** + * Manage power, direction, and timers for one WeDo 2.0 motor. + */ +class WeDo2Motor { + /** + * Construct a WeDo2Motor instance. + * @param {WeDo2} parent - the WeDo 2.0 device which owns this motor. + * @param {int} index - the zero-based index of this motor on its parent device. + */ + constructor (parent, index) { + /** + * The WeDo 2.0 device which owns this motor. + * @type {WeDo2} + * @private + */ + this._parent = parent; + + /** + * The zero-based index of this motor on its parent device. + * @type {int} + * @private + */ + this._index = index; + + /** + * This motor's current direction: 1 for "this way" or -1 for "that way" + * @type {number} + * @private + */ + this._direction = 1; + + /** + * This motor's current power level, in the range [0,100]. + * @type {number} + * @private + */ + this._power = 100; + + /** + * Is this motor currently moving? + * @type {boolean} + * @private + */ + this._isOn = false; + + /** + * If the motor has been turned on or is actively braking for a specific duration, this is the timeout ID for + * the end-of-action handler. Cancel this when changing plans. + * @type {Object} + * @private + */ + this._pendingTimeoutId = null; + + this.startBraking = this.startBraking.bind(this); + this.setMotorOff = this.setMotorOff.bind(this); + } + + /** + * @return {number} - the duration of active braking after a call to startBraking(). Afterward, turn the motor off. + * @constructor + */ + static get BRAKE_TIME_MS () { + return 1000; + } + + /** + * @return {int} - this motor's current direction: 1 for "this way" or -1 for "that way" + */ + get direction () { + return this._direction; + } + + /** + * @param {int} value - this motor's new direction: 1 for "this way" or -1 for "that way" + */ + set direction (value) { + if (value < 0) { + this._direction = -1; + } else { + this._direction = 1; + } + } + + /** + * @return {int} - this motor's current power level, in the range [0,100]. + */ + get power () { + return this._power; + } + + /** + * @param {int} value - this motor's new power level, in the range [0,100]. + */ + set power (value) { + this._power = Math.max(0, Math.min(value, 100)); + } + + /** + * @return {boolean} - true if this motor is currently moving, false if this motor is off or braking. + */ + get isOn () { + return this._isOn; + } + + /** + * Turn this motor on indefinitely. + */ + setMotorOn () { + this._parent._send('motorOn', {motorIndex: this._index, power: this._direction * this._power}); + this._isOn = true; + this._clearTimeout(); + } + + /** + * Turn this motor on for a specific duration. + * @param {number} milliseconds - run the motor for this long. + */ + setMotorOnFor (milliseconds) { + milliseconds = Math.max(0, milliseconds); + this.setMotorOn(); + this._setNewTimeout(this.startBraking, milliseconds); + } + + /** + * Start active braking on this motor. After a short time, the motor will turn off. + */ + startBraking () { + this._parent._send('motorBrake', {motorIndex: this._index}); + this._isOn = false; + this._setNewTimeout(this.setMotorOff, WeDo2Motor.BRAKE_TIME_MS); + } + + /** + * Turn this motor off. + */ + setMotorOff () { + this._parent._send('motorOff', {motorIndex: this._index}); + this._isOn = false; + } + + /** + * Clear the motor action timeout, if any. Safe to call even when there is no pending timeout. + * @private + */ + _clearTimeout () { + if (this._pendingTimeoutId !== null) { + clearTimeout(this._pendingTimeoutId); + this._pendingTimeoutId = null; + } + } + + /** + * Set a new motor action timeout, after clearing an existing one if necessary. + * @param {Function} callback - to be called at the end of the timeout. + * @param {int} delay - wait this many milliseconds before calling the callback. + * @private + */ + _setNewTimeout (callback, delay) { + this._clearTimeout(); + const timeoutID = setTimeout(() => { + if (this._pendingTimeoutId === timeoutID) { + this._pendingTimeoutId = null; + } + callback(); + }, delay); + this._pendingTimeoutId = timeoutID; + } +} + +/** + * Manage communication with a WeDo 2.0 device over a Device Manager client socket. + */ +class WeDo2 { + + /** + * @return {string} - the type of Device Manager device socket that this class will handle. + */ + static get DEVICE_TYPE () { + return 'wedo2'; + } + + /** + * Construct a WeDo2 communication object. + * @param {Socket} socket - the socket for a WeDo 2.0 device, as provided by a Device Manager client. + */ + constructor (socket) { + /** + * The socket-IO socket used to communicate with the Device Manager about this device. + * @type {Socket} + * @private + */ + this._socket = socket; + + /** + * The motors which this WeDo 2.0 could possibly have. + * @type {[WeDo2Motor]} + * @private + */ + this._motors = [new WeDo2Motor(this, 0), new WeDo2Motor(this, 1)]; + + /** + * The most recently received value for each sensor. + * @type {Object.} + * @private + */ + this._sensors = { + tiltX: 0, + tiltY: 0, + distance: 0 + }; + + this._onSensorChanged = this._onSensorChanged.bind(this); + this._onDisconnect = this._onDisconnect.bind(this); + + this._connectEvents(); + } + + /** + * Manually dispose of this object. + */ + dispose () { + this._disconnectEvents(); + } + + /** + * @return {number} - the latest value received for the tilt sensor's tilt about the X axis. + */ + get tiltX () { + return this._sensors.tiltX; + } + + /** + * @return {number} - the latest value received for the tilt sensor's tilt about the Y axis. + */ + get tiltY () { + return this._sensors.tiltY; + } + + /** + * @return {number} - the latest value received from the distance sensor. + */ + get distance () { + return this._sensors.distance; + } + + /** + * Access a particular motor on this device. + * @param {int} index - the zero-based index of the desired motor. + * @return {WeDo2Motor} - the WeDo2Motor instance, if any, at that index. + */ + motor (index) { + return this._motors[index]; + } + + /** + * Set the WeDo 2.0 hub's LED to a specific color. + * @param {int} rgb - a 24-bit RGB color in 0xRRGGBB format. + */ + setLED (rgb) { + this._send('setLED', {rgb}); + } + + /** + * Play a tone from the WeDo 2.0 hub for a specific amount of time. + * @param {int} tone - the pitch of the tone, in Hz. + * @param {int} milliseconds - the duration of the note, in milliseconds. + */ + playTone (tone, milliseconds) { + this._send('playTone', {tone, ms: milliseconds}); + } + + /** + * Stop the tone playing from the WeDo 2.0 hub, if any. + */ + stopTone () { + this._send('stopTone'); + } + + /** + * Attach event handlers to the device socket. + * @private + */ + _connectEvents () { + this._socket.on('sensorChanged', this._onSensorChanged); + this._socket.on('deviceWasClosed', this._onDisconnect); + this._socket.on('disconnect', this._onDisconnect); + } + + /** + * Detach event handlers from the device socket. + * @private + */ + _disconnectEvents () { + this._socket.off('sensorChanged', this._onSensorChanged); + this._socket.off('deviceWasClosed', this._onDisconnect); + this._socket.off('disconnect', this._onDisconnect); + } + + /** + * Store the sensor value from an incoming 'sensorChanged' event. + * @param {object} event - the 'sensorChanged' event. + * @property {string} sensorName - the name of the sensor which changed. + * @property {number} sensorValue - the new value of the sensor. + * @private + */ + _onSensorChanged (event) { + this._sensors[event.sensorName] = event.sensorValue; + } + + /** + * React to device disconnection. May be called more than once. + * @private + */ + _onDisconnect () { + this._disconnectEvents(); + } + + /** + * Send a message to the device socket. + * @param {string} message - the name of the message, such as 'playTone'. + * @param {object} [details] - optional additional details for the message, such as tone duration and pitch. + * @private + */ + _send (message, details) { + this._socket.emit(message, details); + } +} + +/** + * Scratch 3.0 blocks to interact with a LEGO WeDo 2.0 device. + */ +class Scratch3WeDo2Blocks { + + /** + * @return {string} - the name of this extension. + */ + static get EXTENSION_NAME () { + return 'wedo2'; + } + + /** + * Construct a set of WeDo 2.0 blocks. + * @param {Runtime} runtime - the Scratch 3.0 runtime. + */ + constructor (runtime) { + /** + * The Scratch 3.0 runtime. + * @type {Runtime} + */ + this.runtime = runtime; + + this.runtime.HACK_WeDo2Blocks = this; + } + + /** + * Use the Device Manager client to attempt to connect to a WeDo 2.0 device. + */ + connect () { + if (this._device || this._finder) { + return; + } + const deviceManager = this.runtime.ioDevices.deviceManager; + const finder = this._finder = + deviceManager.searchAndConnect(Scratch3WeDo2Blocks.EXTENSION_NAME, WeDo2.DEVICE_TYPE); + this._finder.promise.then( + socket => { + if (this._finder === finder) { + this._finder = null; + this._device = new WeDo2(socket); + } else { + log.warn('Ignoring success from stale WeDo 2.0 connection attempt'); + } + }, + reason => { + if (this._finder === finder) { + this._finder = null; + log.warn(`WeDo 2.0 connection failed: ${reason}`); + } else { + log.warn('Ignoring failure from stale WeDo 2.0 connection attempt'); + } + }); + } +} + +module.exports = Scratch3WeDo2Blocks; diff --git a/src/engine/runtime.js b/src/engine/runtime.js index c938c37e..dd037c57 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -19,7 +19,8 @@ const defaultBlockPackages = { scratch3_sound: require('../blocks/scratch3_sound'), scratch3_sensing: require('../blocks/scratch3_sensing'), scratch3_data: require('../blocks/scratch3_data'), - scratch3_procedures: require('../blocks/scratch3_procedures') + scratch3_procedures: require('../blocks/scratch3_procedures'), + scratch3_wedo2: require('../blocks/scratch3_wedo2') }; /** From 06fe701624d3954bf67aefddbd6d5fd2fa0268ac Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 5 May 2017 15:05:21 -0700 Subject: [PATCH 372/721] Implement WeDo 2.0 blocks --- src/blocks/scratch3_wedo2.js | 312 +++++++++++++++++++++++++++++++++++ 1 file changed, 312 insertions(+) diff --git a/src/blocks/scratch3_wedo2.js b/src/blocks/scratch3_wedo2.js index ac945ef8..bd9e6c11 100644 --- a/src/blocks/scratch3_wedo2.js +++ b/src/blocks/scratch3_wedo2.js @@ -1,3 +1,4 @@ +const color = require('../util/color'); const log = require('../util/log'); /** @@ -328,6 +329,42 @@ class WeDo2 { } } +/** + * Enum for motor specification. + * @readonly + * @enum {string} + */ +const MotorID = { + DEFAULT: 'motor', + A: 'motor A', + B: 'motor B', + ALL: 'all motors' +}; + +/** + * Enum for motor direction specification. + * @readonly + * @enum {string} + */ +const MotorDirection = { + FORWARD: 'this way', + BACKWARD: 'that way', + REVERSE: 'reverse' +}; + +/** + * Enum for tilt sensor direction. + * @readonly + * @enum {string} + */ +const TiltDirection = { + UP: 'up', + DOWN: 'down', + LEFT: 'left', + RIGHT: 'right', + ANY: 'any' +}; + /** * Scratch 3.0 blocks to interact with a LEGO WeDo 2.0 device. */ @@ -340,6 +377,13 @@ class Scratch3WeDo2Blocks { return 'wedo2'; } + /** + * @return {number} - the tilt sensor counts as "tilted" if its tilt angle meets or exceeds this threshold. + */ + static get TILT_THRESHOLD () { + return 15; + } + /** * Construct a set of WeDo 2.0 blocks. * @param {Runtime} runtime - the Scratch 3.0 runtime. @@ -382,6 +426,274 @@ class Scratch3WeDo2Blocks { } }); } + + /** + * Retrieve the block primitives implemented by this package. + * @return {object.} Mapping of opcode to Function. + */ + getPrimitives () { + return { + wedo2_motorOnFor: this.motorOnFor, + wedo2_motorOn: this.motorOn, + wedo2_motorOff: this.motorOff, + wedo2_startMotorPower: this.startMotorPower, + wedo2_setMotorDirection: this.setMotorDirection, + wedo2_setLightHue: this.setLightHue, + wedo2_playNoteFor: this.playNoteFor, + wedo2_whenDistance: this.whenDistance, + wedo2_whenTilted: this.whenTilted, + wedo2_getDistance: this.getDistance, + wedo2_isTilted: this.isTilted, + wedo2_getTiltAngle: this.getTiltAngle + }; + } + + /** + * Turn specified motor(s) on for a specified duration. + * @param {object} args - the block's arguments. + * @property {MotorID} MOTOR_ID - the motor(s) to activate. + * @property {int} DURATION - the amount of time to run the motors. + * @return {Promise} - a promise which will resolve at the end of the duration. + */ + motorOnFor (args) { + return new Promise(resolve => { + this._forEachMotor(args.MOTOR_ID, motorIndex => { + this._device.motor(motorIndex).setMotorOnFor(args.DURATION); + }); + + // Ensure this block runs for a fixed amount of time even when no device is connected. + setTimeout(resolve, args.DURATION); + }); + } + + /** + * Turn specified motor(s) on indefinitely. + * @param {object} args - the block's arguments. + * @property {MotorID} MOTOR_ID - the motor(s) to activate. + */ + motorOn (args) { + this._forEachMotor(args.MOTOR_ID, motorIndex => { + this._device.motor(motorIndex).setMotorOn(); + }); + } + + /** + * Turn specified motor(s) off. + * @param {object} args - the block's arguments. + * @property {MotorID} MOTOR_ID - the motor(s) to deactivate. + */ + motorOff (args) { + this._forEachMotor(args.MOTOR_ID, motorIndex => { + this._device.motor(motorIndex).setMotorOff(); + }); + } + + /** + * Turn specified motor(s) off. + * @param {object} args - the block's arguments. + * @property {MotorID} MOTOR_ID - the motor(s) to be affected. + * @property {int} POWER - the new power level for the motor(s). + */ + startMotorPower (args) { + this._forEachMotor(args.MOTOR_ID, motorIndex => { + const motor = this._device.motor(motorIndex); + motor.power = args.POWER; + motor.setMotorOn(); + }); + } + + /** + * Set the direction of rotation for specified motor(s). + * If the direction is 'reverse' the motor(s) will be reversed individually. + * @param {object} args - the block's arguments. + * @property {MotorID} MOTOR_ID - the motor(s) to be affected. + * @property {MotorDirection} DIRECTION - the new direction for the motor(s). + */ + setMotorDirection (args) { + this._forEachMotor(args.MOTOR_ID, motorIndex => { + const motor = this._device.motor(motorIndex); + switch (args.DIRECTION) { + case MotorDirection.FORWARD: + motor.direction = 1; + break; + case MotorDirection.BACKWARD: + motor.direction = -1; + break; + case MotorDirection.REVERSE: + motor.direction = -motor.direction; + break; + default: + log.warn(`Unknown motor direction in setMotorDirection: ${args.DIRECTION}`); + break; + } + }); + } + + /** + * Set the LED's hue. + * @param {object} args - the block's arguments. + * @property {number} HUE - the hue to set, in the range [0,100]. + */ + setLightHue (args) { + // Convert from [0,100] to [0,360] + const hue = args.HUE * 360 / 100; + + const rgbObject = color.hsvToRgb({h: hue, s: 1, v: 1}); + + const rgbDecimal = color.rgbToDecimal(rgbObject); + + this._device.setLED(rgbDecimal); + } + + /** + * Make the WeDo 2.0 hub play a MIDI note for the specified duration. + * @param {object} args - the block's arguments. + * @property {number} NOTE - the MIDI note to play. + * @property {number} DURATION - the duration of the note, in seconds. + * @return {Promise} - a promise which will resolve at the end of the duration. + */ + playNoteFor (args) { + return new Promise(resolve => { + const durationMS = args.DURATION * 1000; + const tone = this._noteToTone(args.NOTE); + this._device.playTone(tone, durationMS); + + // Ensure this block runs for a fixed amount of time even when no device is connected. + setTimeout(resolve, durationMS); + }); + } + + /** + * Compare the distance sensor's value to a reference. + * @param {object} args - the block's arguments. + * @property {string} OP - the comparison operation: '<' or '>'. + * @property {number} REFERENCE - the value to compare against. + * @return {boolean} - the result of the comparison, or false on error. + */ + whenDistance (args) { + switch (args.OP) { + case '<': + return this._device.distance < args.REFERENCE; + case '>': + return this._device.distance > args.REFERENCE; + default: + log.warn(`Unknown comparison operator in whenDistance: ${args.OP}`); + return false; + } + } + + /** + * Test whether the tilt sensor is currently tilted. + * @param {object} args - the block's arguments. + * @property {TiltDirection} DIRECTION - the tilt direction to test (up, down, left, right, or any). + * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction. + */ + whenTilted (args) { + return this._isTilted(args.DIRECTION); + } + + /** + * @return {number} - the distance sensor's value, scaled to the [0,100] range. + */ + getDistance () { + return this._device.distance * 10; + } + + /** + * Test whether the tilt sensor is currently tilted. + * @param {object} args - the block's arguments. + * @property {TiltDirection} DIRECTION - the tilt direction to test (up, down, left, right, or any). + * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction. + */ + isTilted (args) { + return this._isTilted(args.DIRECTION); + } + + /** + * @param {object} args - the block's arguments. + * @property {TiltDirection} DIRECTION - the direction (up, down, left, right) to check. + * @return {number} - the tilt sensor's angle in the specified direction. + * Note that getTiltAngle(up) = -getTiltAngle(down) and getTiltAngle(left) = -getTiltAngle(right). + */ + getTiltAngle (args) { + return this._getTiltAngle(args.DIRECTION); + } + + /** + * Test whether the tilt sensor is currently tilted. + * @param {TiltDirection} direction - the tilt direction to test (up, down, left, right, or any). + * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction. + * @private + */ + _isTilted (direction) { + switch (direction) { + case TiltDirection.ANY: + return (Math.abs(this._device.tiltX) >= Scratch3WeDo2Blocks.TILT_THRESHOLD) || + (Math.abs(this._device.tiltY) >= Scratch3WeDo2Blocks.TILT_THRESHOLD); + default: + return this._getTiltAngle(direction) >= Scratch3WeDo2Blocks.TILT_THRESHOLD; + } + } + + /** + * @param {TiltDirection} direction - the direction (up, down, left, right) to check. + * @return {number} - the tilt sensor's angle in the specified direction. + * Note that getTiltAngle(up) = -getTiltAngle(down) and getTiltAngle(left) = -getTiltAngle(right). + * @private + */ + _getTiltAngle (direction) { + switch (direction) { + case TiltDirection.UP: + return -this._device.tiltY; + case TiltDirection.DOWN: + return this._device.tiltY; + case TiltDirection.LEFT: + return -this._device.tiltX; + case TiltDirection.RIGHT: + return this._device.tiltX; + default: + log.warn(`Unknown tilt direction in _getTiltAngle: ${direction}`); + } + } + + /** + * Call a callback for each motor indexed by the provided motor ID. + * @param {MotorID} motorID - the ID specifier. + * @param {Function} callback - the function to call with the numeric motor index for each motor. + * @private + */ + _forEachMotor (motorID, callback) { + let motors; + switch (motorID) { + case MotorID.A: + motors = [0]; + break; + case MotorID.B: + motors = [1]; + break; + case MotorID.ALL: + case MotorID.DEFAULT: + motors = [0, 1]; + break; + default: + log.warn(`Invalid motor ID: ${motorID}`); + motors = []; + break; + } + for (const index of motors) { + callback(index); + } + } + + /** + * @param {number} midiNote - the MIDI note value to convert. + * @return {number} - the frequency, in Hz, corresponding to that MIDI note value. + * @private + */ + _noteToTone (midiNote) { + // Note that MIDI note 69 is A4, 440 Hz + return 440 * Math.pow(2, (midiNote - 69) / 12); + } } module.exports = Scratch3WeDo2Blocks; From cbfbc5d600bfa2e1f57a925bfde831be8a21c1cf Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Thu, 11 May 2017 22:39:24 -0700 Subject: [PATCH 373/721] Convert motor duration: seconds -> milliseconds The block takes seconds, whereas the device takes milliseconds. Turning on a motor for 1 millisecond isn't very dramatic. --- src/blocks/scratch3_wedo2.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/blocks/scratch3_wedo2.js b/src/blocks/scratch3_wedo2.js index bd9e6c11..d79ef909 100644 --- a/src/blocks/scratch3_wedo2.js +++ b/src/blocks/scratch3_wedo2.js @@ -456,13 +456,14 @@ class Scratch3WeDo2Blocks { * @return {Promise} - a promise which will resolve at the end of the duration. */ motorOnFor (args) { + const durationMS = args.DURATION * 1000; return new Promise(resolve => { this._forEachMotor(args.MOTOR_ID, motorIndex => { - this._device.motor(motorIndex).setMotorOnFor(args.DURATION); + this._device.motor(motorIndex).setMotorOnFor(durationMS); }); // Ensure this block runs for a fixed amount of time even when no device is connected. - setTimeout(resolve, args.DURATION); + setTimeout(resolve, durationMS); }); } From 77cc01a38ad322e40b0adbce0a5b05a0a015fe76 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 25 May 2017 11:44:49 -0400 Subject: [PATCH 374/721] Add variable creation and variable serialization --- src/engine/blocks.js | 6 +----- src/engine/variable.js | 4 ++++ src/virtual-machine.js | 24 +++++++++++++++++++++--- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index ebe113f8..13baf953 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -427,11 +427,7 @@ class Blocks { * @return {string} String of XML representing this object's blocks. */ toXML () { - let xmlString = ''; - for (let i = 0; i < this._scripts.length; i++) { - xmlString += this.blockToXML(this._scripts[i]); - } - return `${xmlString}`; + return this._scripts.map(script => this.blockToXML(script)).join(); } /** diff --git a/src/engine/variable.js b/src/engine/variable.js index 2be2c0f8..69cb915c 100644 --- a/src/engine/variable.js +++ b/src/engine/variable.js @@ -15,6 +15,10 @@ class Variable { this.value = value; this.isCloud = isCloud; } + + toXML () { + return `${this.name}`; + } } module.exports = Variable; diff --git a/src/virtual-machine.js b/src/virtual-machine.js index fef841a6..19a4a8dd 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -491,9 +491,18 @@ class VirtualMachine extends EventEmitter { * of the current editing target's blocks. */ emitWorkspaceUpdate () { - this.emit('workspaceUpdate', { - xml: this.editingTarget.blocks.toXML() - }); + // @todo Include variables scoped to editing target also. + const variableMap = this.runtime.getTargetForStage().variables; + const variables = Object.keys(variableMap).map(k => variableMap[k]); + + const xmlString = ` + + ${variables.map(v => v.toXML()).join()} + + ${this.editingTarget.blocks.toXML()} + `; + + this.emit('workspaceUpdate', {xml: xmlString}); } /** @@ -538,6 +547,15 @@ class VirtualMachine extends EventEmitter { postSpriteInfo (data) { this.editingTarget.postSpriteInfo(data); } + + /** + * Create a variable by name. + * @todo this only creates global variables by putting them on the stage + * @param {string} name The name of the variable + */ + createVariable (name) { + this.runtime.getTargetForStage().lookupOrCreateVariable(name); + } } module.exports = VirtualMachine; From 55f677702aaac827c38d8dadf4ba5f307ad7abb6 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 30 May 2017 10:05:13 -0400 Subject: [PATCH 375/721] add missing file --- src/engine/monitor-record.js | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/engine/monitor-record.js diff --git a/src/engine/monitor-record.js b/src/engine/monitor-record.js new file mode 100644 index 00000000..76ed0848 --- /dev/null +++ b/src/engine/monitor-record.js @@ -0,0 +1,10 @@ +const {Record} = require('immutable'); + +const MonitorRecord = Record({ + id: null, + opcode: null, + value: null, + params: null +}); + +module.exports = MonitorRecord; From 875eba568bc74f93a941d695f0c1ea94e2869dc4 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 30 May 2017 10:24:09 -0400 Subject: [PATCH 376/721] fix file name in test --- test/unit/engine_runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/engine_runtime.js b/test/unit/engine_runtime.js index 6ac6aaab..458f3884 100644 --- a/test/unit/engine_runtime.js +++ b/test/unit/engine_runtime.js @@ -1,6 +1,6 @@ const test = require('tap').test; const Runtime = require('../../src/engine/runtime'); -const MonitorRecord = require('../../src/engine/records'); +const MonitorRecord = require('../../src/engine/monitor-record'); const {Map} = require('immutable'); test('spec', t => { From d3a4a7b81a9c68d53e3bbcafa43f3bea3da19560 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 1 Jun 2017 12:31:24 -0400 Subject: [PATCH 377/721] Pin to `latest` for 3.0 modules Reverts #551 Pinning to `^0.1.0.prerelease.0` is not equivalent to `latest`, since has the effect of not installing the latest version on `npm install`, you have to also do `npm update`. This hurts us ergonomically on `npm install`, and doesn't help us since we already are releasing breaking changes on `prerelease`. --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 7314b7b8..ff1f895f 100644 --- a/package.json +++ b/package.json @@ -42,9 +42,9 @@ "lodash.defaultsdeep": "4.6.0", "minilog": "3.1.0", "promise": "7.1.1", - "scratch-audio": "^0.1.0-prerelease.0", - "scratch-blocks": "^0.1.0-prerelease.0", - "scratch-render": "^0.1.0-prerelease.0", + "scratch-audio": "latest", + "scratch-blocks": "latest", + "scratch-render": "latest", "scratch-storage": "^0.1.0", "script-loader": "0.7.0", "socket.io-client": "1.7.3", From 467b747283afdd610cc1ac8b8bf8e31f074fa2ad Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Fri, 2 Jun 2017 08:57:21 -0400 Subject: [PATCH 378/721] Bump scratch-storage version Now 0.2.0 is the lowest compatible version. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0308aa6f..11fb66ef 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "scratch-audio": "^0.1.0-prerelease.0", "scratch-blocks": "^0.1.0-prerelease.0", "scratch-render": "^0.1.0-prerelease.0", - "scratch-storage": "^0.1.0", + "scratch-storage": "^0.2.0", "script-loader": "0.7.0", "stats.js": "^0.17.0", "tap": "^10.2.0", From ab0cef52eb759bad7e7aad3d637fa31a19b1c18c Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 5 Jun 2017 16:55:15 -0400 Subject: [PATCH 379/721] Change wait to use a promise instead of being called every frame --- src/blocks/scratch3_control.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/blocks/scratch3_control.js b/src/blocks/scratch3_control.js index f3b53baa..f460399e 100644 --- a/src/blocks/scratch3_control.js +++ b/src/blocks/scratch3_control.js @@ -73,18 +73,13 @@ class Scratch3ControlBlocks { util.startBranch(1, true); } - wait (args, util) { - if (!util.stackFrame.timer) { - util.stackFrame.timer = new Timer(); - util.stackFrame.timer.start(); - util.yield(); - this.runtime.requestRedraw(); - } else { - const duration = Math.max(0, 1000 * Cast.toNumber(args.DURATION)); - if (util.stackFrame.timer.timeElapsed() < duration) { - util.yield(); - } - } + wait (args) { + const duration = Math.max(0, 1000 * Cast.toNumber(args.DURATION)); + return new Promise(resolve => { + setTimeout(() => { + resolve(); + }, duration); + }); } if (args, util) { From e237d2484611298d2c8faf494f20b1ec431ea270 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Tue, 6 Jun 2017 10:21:27 -0400 Subject: [PATCH 380/721] fix lint --- src/blocks/scratch3_control.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/blocks/scratch3_control.js b/src/blocks/scratch3_control.js index f460399e..975dfeb8 100644 --- a/src/blocks/scratch3_control.js +++ b/src/blocks/scratch3_control.js @@ -1,5 +1,4 @@ const Cast = require('../util/cast'); -const Timer = require('../util/timer'); class Scratch3ControlBlocks { constructor (runtime) { From 40345384aa47e267115b9a201ab1e93bd6e1535f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 7 Jun 2017 09:06:46 -0400 Subject: [PATCH 381/721] Select the last target instead of the first --- src/virtual-machine.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 19a4a8dd..436ff60e 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -379,7 +379,8 @@ class VirtualMachine extends EventEmitter { this.runtime.disposeTarget(sprite.clones[i]); // Ensure editing target is switched if we are deleting it. if (clone === currentEditingTarget) { - this.setEditingTarget(this.runtime.targets[0].id); + const lastTargetIndex = this.runtime.targets.length - 1; + this.setEditingTarget(this.runtime.targets[lastTargetIndex].id); } } // Sprite object should be deleted by GC. From 454082b569e6cb1969ede222ba5fb771a4288e9c Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Wed, 7 Jun 2017 17:05:24 -0700 Subject: [PATCH 382/721] Improve cleanup on clone disposal The `dispose()` method on `RenderedTarget` now: - informs the runtime that it should end any threads corresponding the target being disposed, and - removes the clone from its sprite. --- src/sprites/rendered-target.js | 2 ++ src/sprites/sprite.js | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 0bc5cff6..2fb0ddf9 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -832,6 +832,8 @@ class RenderedTarget extends Target { */ dispose () { this.runtime.changeCloneCounter(-1); + this.runtime.stopForTarget(this); + this.sprite.removeClone(this); if (this.renderer && this.drawableID !== null) { this.renderer.destroyDrawable(this.drawableID); if (this.visible) { diff --git a/src/sprites/sprite.js b/src/sprites/sprite.js index 3a0402a1..293a1f26 100644 --- a/src/sprites/sprite.js +++ b/src/sprites/sprite.js @@ -58,6 +58,18 @@ class Sprite { } return newClone; } + + /** + * Disconnect a clone from this sprite. The clone is unmodified. + * In particular, the clone's dispose() method is not called. + * @param {!RenderedTarget} clone - the clone to be removed. + */ + removeClone (clone) { + const cloneIndex = this.clones.indexOf(clone); + if (cloneIndex >= 0) { + this.clones.splice(cloneIndex, 1); + } + } } module.exports = Sprite; From 6320fd72c311a65547adeb89e37c80c585f1c2b8 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Thu, 8 Jun 2017 11:16:29 -0700 Subject: [PATCH 383/721] Add test for clone cleanup This new test verifies that clones and their associated threads are cleaned up properly by the `delete this clone` block. The clones run two stacks each: one which waits and then deletes the clone, and another which includes a `forever` loop: this is to verify that the thread running the `forever` loop is ended when the clone itself is deleted. The project does this with two batches of clones to ensure there are no problems with reusing array indices previously occupied by now-removed threads or clones. --- test/fixtures/clone-cleanup.sb2 | Bin 0 -> 54947 bytes test/integration/clone-cleanup.js | 96 ++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 test/fixtures/clone-cleanup.sb2 create mode 100644 test/integration/clone-cleanup.js diff --git a/test/fixtures/clone-cleanup.sb2 b/test/fixtures/clone-cleanup.sb2 new file mode 100644 index 0000000000000000000000000000000000000000..61baeb7f2def6182f3372a4d89edcee7bf23db88 GIT binary patch literal 54947 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

ZA_ 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

xPuw%4EP#DQjg(Qp1B<;*+zib$#yev7@Iz)0s z8E@+7$ptfrKDpQ zn@daWN8iDvi7tzKil++SaZfYF^smTM_zrXqdIyOi4b>Q+yS3(tni;a`ZIj!E$Y1J? zJBmZU(Ch4Tf>O~%QBY7tGO~&E9_U1v4*dilko{9%P~qS;_c=?dwo*2$?OAI;%F}MO zZ3tu{1+0hs<)ZDPp~7_1S6rm`LYKkYz@gv>67`Q!4S_zM1J<6p$MUkay{!~!rh144 z@;RWsj8VKgp;HtRUL$2clemfAginATAO^ezv``0yPLox+BMldoOlglclVq6cC)pix zFvuaUkTtI<;tiso{MDRD5{GZoMnOlxTVMp}qdp1MctMB7z*YTD>IeBCNGUSDcD<&) z#our;gdauKqPK#P+%wDz_&>-Xm`Un}7i57piq7A~HNngzZCcybQ}RRJN5980E3_94 zvIxO{qDP{Cg?)INSZQ=CvJ_T>hrylTRUn)?KM?Q!X}PFnD^^KjCFAAMI>Me4yn-BI zg$vG!dW*Y@F7cnRPZKKC3(tW*fi`e5sHWZH9gWXM-+aA+sw9Cg}G}s&Pgsi?j&gCYJS})6$gh|gS=NL7vCaMR|h+sUVhi^U;PieMq< zHPMWohgXvMfi|E4xIl$NJ-ol$;|uz^!C=;}rl5xIzQ`_njfrL`@I5N%BTIRfU<3yG_(HcpB%rV2kLNsGl&3x087a zKS&FbvF1A9B~U<0k!+pl;#)Rq+bfc#C!{l!Ukp~~Ac`DKXB+qhq&8LvbNFM~W_nLF z0sb3I1#?N6jG^K|nrFZDj1E`cl}1X#$ZYF>_A$Y9DWQ$PjH4uH{SEmkAsC7=8j$ z$qb$bU;tQ1;7f7*Wz16#BICdlvS;cC=6X*S_!ZCL==k@AuY`8~XHF^O1@?+I75W1_ z4Xy@x;4um#Fx~Z+`GY1)aah`3R;WTvDem{w_t>B8)BGx-O}I;No%=6y8NM3%0i6VI zfG@xyU?~*|W_t!%U+6w5=gWG^)+%WRuVY~dLtRV@?;l|s8D|aSohB=rWQY;+0bEc( z^0?E~y}?!9nf6M9P34uXmp+!;G~KOxe4C*HLd+d5xGyBDe)%FYgVzIl3qK;IXawXy zhg(yuH96Fi!o9r3k`YBm!UD}Ox7R#a^YUlN?{3^0V$*t=r?FKm=8V$_kitz zZlM8Qug$6-tolv1Qfel-)h*jszY0FcP;%phnW7TmMLx<&Wu%ina1x1(*U8+|W00V( z@&Dmun|`UgkkUUYYfvl9soq{-5&b*IB)BfRA-W+b;*MaJ;TcFK+y%M=DIhi2Ks`XR zZ%y+)HwXuc!bxAzhjpDEh0*hX=LnkoA-v5Ol(Hm(F&odP&c?8nd$9I+3nNW zDaIsqrW}!NP|VOBw{ZeBq@A&O3V~R3Ot^p_%b_tEup-(J_$8DGN5F%jG^*0?aiZpn zn#qa|vXSzDy2jk%IYY+bY|eN=i4YQX6ZGVkFt^}4kXVxKv_K=t=wdB!GRXH})~UJ` z%Ih*reo1x9*y!3#X+aOOKJx|(X9>p(nz-Frv*}9GSEQ0_ybR5NHUdeZzTWe;Dl+T( zU6w5?R+Q`V?L7i@a13J}ca~tc@T1^7uM@jHaSHtikA@he9JfN9!8l4E-)cvJ@gMaB z`Aunoe6mJut|fB?k@zOIjQ3bjDM%+hjGq{Q<&kIB8F~tpk-4s!RD{gNm6-==UnyqF zj>~?j?inLoijW1F$gJlU@@ES6@h5R=$Xr1y4TbZdhfn}o4$T7Q2j_aKt$h7c#mpPE!sMTSGro( zK^0>xa&`!vL^?1txKaE={DHg`>;t65bf>L^;-Py`JbVjU4QPYkJz2Jc`bgC^*$(Lp z#RT0>+jf5)1QI*hlX+ix!+4pTHH>szM=OH<1%qVv?*Vic+)tV7hn>SsSsJRMLfR-> zr;ai2boZhrVr|SB+`)V?e;)TFnf<$h-hjtKAf$wb!4n_`KnYHB53u~wN)(-CV%b&Y zQ3LswC>WrfVqD_5czyVtcn{eF8I@Qy?G?#uzd`@NIJ_Aw3{`j$dzxX7s)PKK^oqi* zonU+Hi-JUS5&I-}Cy&MZ#s0$Rj^9PpP$sksN`_Nl3y4w|`OBUAO*b^-6ckx4869sl z`CK8&NAv}A8CS-O=g;QeWqqa}ME^(o2a1EA!6>aaTnRi2rhCR(kCGXl<+4FClQP-x zucKXX9PJ4)k<*pef$!rMvU@PxSZDHv=nb?5X4CvI7n(ykTRqM+ z#$^x1qXU`Uxi;QFzKXk!{hnA&=0k75d*E!^KePneeKJBT@%AQfi6*GYdT)7y^0oem z9S_WgyAog6e(q;p9rqG@CSxXfAEk!#;bm|${1cu6^`<`Z|L2@XmYW_{RLS}(x@!+w z(!DZZ0$$Jh!0pVx&dcJ)vwZZ?ST^z-Z6!^JbVKISrh{ukXFcDoJ@k83Pvy~yYV|17 zHrMu$2z|_W!nwzj^4hr9*#j6m@f7q6Z7;2e)&)64^MM;Fr+h!{tBqCaC`F8Xkg`d4 z)@JbCg%;4&toz&ryjxre`zPZ7J{YBtaW47(iROk)V0$Vf@YprLqS7_1`YQS>KB@_m z!+9>$8Tm*oWan}(ao=*Dvh>6ctPtryQ^Rj*W04!QK~M`t;G1qQGSsQhC@6|gs;!2e zj$VO#u!m0GcXNZ>-?<;zM;Uf}H;N*QX$aDYv?4s(KL8P=x%Qgn+TNlJ)>ugv_L+gIK89U21ut8>sprpQxZ~o|&$? z#FW8E5+j2hhn!JFcoy_>`}8Ppw+79{3Bp z&ZKeTxE;8OoZHNH#B+>?PDU;w)o2ivB3cNgO!W@5r5diQzboD-9;vI0JDhWZmGDIR zC*~h)IeQBG3%SN*oQFOot#bq-MbZ!p6r_m!ogIse>6#+Nc6m?b1lg8{q^t7uSFHEFHB=6|oOr@ivT9jc z=4av@o{0U83`341Q_<-t2T_2!U{B9h>l6J0b+MvM`BHP$6z=*xcoUw9pJGI?8d(Eb zZ02=(B{mLCM7YRb$XmoldkEd51buGDcGE)bTjedqG}SD<$@;^a0Ep4aM36a*EoG^h zEyN+b4LyWBprs(Mk)wzi{u3AN%UbhG5^P0 z&NxHAhYd!DAoGz@#D`o$=0F%#?kgj!5lXd$s!(xK)v7ySz2s@25VTkLD8?*iCQHf; zkaf!2u@G_-If3M(e<1B?Qvh{fqdRDMte>LpuiUAu)7&#vIhzA%kQvp})r@%Ncjh_< zgBXgxB;`JVM3H^STUrAMQo8#BcF?qlJhxP(OSM`5)QWmHQuws~cro#dv5J|=tR#BT z2V(*>7MY3sgCrpv;TGz#Kwme{Dl$ybyj9wj8chdNljF7jHZTuy;8aEuQ_URAyh?EC zyRjq`MT1B+QcAOc1(YSe#g22PO6_SCL)l5~)Ge^~^L!5d3m?bM6JHp2nRl5d8L31e z-XGhH&PU_WGYAik12zPHxnbKw<0);Fnyrq|8jX`3u-`^C()QvF1dYjJK}->&2Yn~D z0BuCfNKa%htqBAuR^KIOz9okT5O{H&REMRJw2=fzRzz<_L z(R7rDZb3G~xj^UOZyt-yZ!FV3RdrM4YE~KI?WiwESxf`)?L;l(Cb_p9MhdYMzlia$ zD)cHk2DuA0QTF@)c22R3*Vm~P$`%zzS7Da9rU&msdUOx*A0wSLnRS^tka3T`2A_#V zkTZTJGoE*VyTNOo4)!i4xwcyMyK1I}Z|rG@e9_c-v{m?hVhHm&D~ENAnaFrYzkq+h zmSWZD8bktpr%WZeZV$_My+{3zYPULFztb|oT@{=T>Ck6%JEITl7t6)`#MnW&aRGi6 z8-^W0$I%#IS+K(M!v>h{Yq@HrYPdGalVv zhq;h!cp`O-zs~70yY%BVbJV*us|~$u=RFL{DEKS(ia5sfv4*h+u!4*OL>PS-o`7G+ zsM!B#KY^vePwrIPY4Y5f)Tm~NzQFR-We)hjYp8=RV)kP7W-nzWGoKP;=)G_Q=EU6S z4Wte%39axxu`e`T)Opl*)j{nZ(<;YSzk<3NvE%(1Y*rKNC2IkB-goF{@K+cezl60% z-@$QIDH$*SZYj}U)c92k$ppzJYl^35=sjddE%Y;tNY)Y7Gv;;1ctV2rB=^>gjUw|g zx!{XnxMz@Unek6;cl93iGTm%*f$KtG95@^;rI$1Iv4*lGtYRjc@ru447vn;FEp{2n zhQcWfU%10*YSUGzd#Y2li;e%)kG1qE4w>8mGuXs2eBLnaVz!@i78)T z6SaTfm@CtI$nahhP<>Ia*8gYO?WTviL%&cO5yQOAy3eMtr!iXzfNsKeUFs}b^6UjBj_#Ctg!;91l?iq3^ZyQHA=mMI##iP-a#S%hm9dsS4==$|@F2Dfy#!CE zUh!{rZnb>X@6+5?$7)eyz;@pI2jv315}Qk)%r~qv zg6G@{YnHJuSzQ~Y5$VTUQ1_nTO>iihMW-@8GxxC=tUHVhVm;o5RbqOq4ts`>KO>+N zdZ*YEO%Xc2n*3dpCeHLf$4_4&^$P7Pc7RYZ8kyIa{TM6h7R-v)qe5&77DU)^CB^1z zbc`_1)kUl8l?K&V{ZwnSdsfIyO5`DZA>%o-khzobiLS<8qBY0}bRlX*?!Xyj_W^{g z1uQUl$g12qs@K|n<|60RfCyZVjK^;h9hntOfVq=6fseq_&^c%o%EqFQAbIz3-+SMl zWU~K%TRW}!YJ6f}?n|eJ(Y9ih^cjqi%=e6G#4}uk-AC$3&NKo|LncDIDRN)Aqp$gb zPN4ozIY_-xKib;aqYW;Ga?wKk6TxLtnO{gd+m5x6cq~DFA^i|8yq9Y8k9F;~+|ut> zFHlZVozf09Tb(h1N}vE~haV?=jEl@(j1TnX_)XM6GK(a192q&MfX3h@Pl>JE*h!nD zqN)aHh8dmqhu)Kv4e%p0j-J41U~FWd#C*ICeU3CB9noQ^mCQO9Qoj1EjvBL8cT`=f z;%VXydu-c0vqO`h(`W$CV$5M;OanpCORz=cY$%$BEHVcbwz> z*QsWBKDLqGm0aU^#(fftr(o;Q!{|4(3B5qBaV7PMKiSo6Dc1j|d7v)W+KhkMH+Y*v zw_5B z4Vj5=CfXPPQ$<{*PsZ<%sFOmXfeIy%Sx|qx1St=OlkU^*y{5 z`$k{N=*yhT=ue!*=V0&2oh4(<=vzbvts*1s*{*c!RKq(>zUH%bgz1NUlJ|Wm8*(B| zxSjAYsu)4yI6Vzu zlgy%+F^lLxH(*j!i}u0ooV@fm~oAZ2?w7U&b&n zP==pA2;WN1BSU%ELbCoy53Z!_^o@4jH22oe)MRSz>+YIejxoLtl;zNUg`~qzs0&G^)!R0~I7HX1d8%7rO1Jm&Bm}boI&CO6m0m$OiD~4Y>4f+| z%!l^I=97FYjI7wp4sG(LIXal<>AP#QbRomP)}5~X{uh*u(0QZ|dre?k!}L?m$h_#qv@FajHe)Y9%zJ%&|-Wsv6?8M%Wxez*I+Cci^met zDB2)!c<8qGisOyByJ4BGKp$s%Zad~q4_v3VKwpq%>)OFXLHPl#YoPB+skP-CLO0dWD3B)jB9(@CDA=gLXUCBTFiP4Mj zQXneG^PICKnR4|cvo`!`-r$(yWrhH-2kiygf&)Yp@f-ax{vA7yMdLk4&h&+h!HU59 zpIfSd1^q=S z^QoN=Ei_|`u7_S@Y-a=93;gdW>!9_>T}(?~Mx+pH=;!dU_$BEs2hD)IyH>M%GED1$uhA*e9AN z8y4&1jfXAA99_J4P)nu5XOVcEK|e~rf9XXydtg{^HPIpbD-=d*bQz9yp9I$C9x)Qitbbap-cI7mTH3`6sz5t$b5~p`W3T zDcaWJT;$UQ^T2iReAI|N$LHY)9)tZrAEQsuBWM+pM)QE*DKUZDZoAEBnrLwAHyRa| zYYyD|IdGNwFEj%AzqY;tOp2rFzsIL%cF_ZZ<&Xn{Jk+%>qn1riR|3+`}8kOX%L z?hrIUAlMOtJHg%Ic6Z$SU*!Kj&-=aa_w78rw>!5rJzZT@UG=N(o>jQ1bU>;l4a9ly zWAT{yR%|35=FhQ-^pT*4vsa%IE1hxwL!FNWqx;n><`ef%qS9MAQB20qa3dTQXNz^k z_F}yFM0mqD=N{4JQGGAN?4WjyZ26e*ad>2^Qo*=nkM$>_6lN1QOIRiD5ub}g#BAam zalLq2Ocq-SE|;BA$awdfF+iCd>6TGCQZx3IHpblKChL{>DkoL9)h6!=(ID z4eSULxPA2Ypt{ppUlyYxvofAU`YB}%)}G7}FY8!SNW0yLikl zN6oxWW=Hixgw7a{(JWd|U1db=J^m~-p6SdZF&^K*WuYEX@^G@W7@rWofsD-wswb@H z^wsUr78zwS#z#skonimZrrsdZjDEp>F8nI4z~^ueXk8g7aZ*G4M!3garZW-bY|$@4 z%UWd&h-`}8)e@}>ZX&5mUt_-%R*HXNR_YJ_eorZ0$|{Y;i7@YJ3iF=C!JNGuN}kBw zk6AMcM6;{yjGWFQe+F8{WN@p6E8-kn4DN7{)K2OoeI>1d=(jd&plx2*`c3T?U7xWs zV@z~_THc_Y%|1v`yL~EtbBOuHs5!3%)ONnGADx8atH}k@Sp}k)^TT z+AEWD+XWA(IqVXCrnn3jkWNUHY)GHW@1=o~ClY=bV0;(6N9GQ7YxG4%*GR)y0j;$; z+xa>eNv&ZY@LR-m{JnHux-ET#!RlQf~ZI=mK7Utzf^MS1+OziJpnX#U7|5VOQ8r{ywyV`I>JmF2RN5 z1o>|{L%u1OkgrRZz<)f+Z)d)S7^9teMr|MK9W5Elul}aLwgPt?8AzXH>kIS5KX60f zMMb%({7yO|9mBoFoqPhj7S-{tnvGzGKPfst+DN&tJun3~6bzzdb~ayJEQcwnqjXuy zE$@&HNy!q8+X-9QG1O3BuwQG-WBH=*BWGj%v}$IW(=@0-C9v!G2I4H7D(#g%NG0U* z@@I0obQxC_k8{`Q!$DIgZ1h)JMt4MBMxQAc^$aWQwID_46YP7wt=JL&hHFZzfG2OI z>5_}fiFNo^%r;WPt!|cAYekDjMn=AczOHMIbAI+WpiWF0S5Igr7Q#P6j8;{8iEBs} zUL(E&TznbosH9%h=h_XtV`Kq6lby-$6?Oq$*#aNHyKpXP7a#{& zgih=h$N<*0i|M;!Ya^p0i=(fV-}SxL12;rgQ z+QBTfIZk6^kdi$rMY=^RD%165Rw3{2pgTRAJ-RwyTD?lK`aj&?J<{# zQ3v>{BH98P-Q_ zW$a1ham0vDS8Eul_FL~hnNDZr2tQD4i|a@|q`M&hoIF!v@mS#(b`*8iPqdrpamx9~ z2DryZ%6Gv1dfpAPh~~M1!Vs}HZZ9pB76RQ$u#UO$NMRngl4i+mXO6+CsZlf1I@(y# zv=e4=cX2R)%3#LwCxs&avZn>4X3|Z-HrBz+>k>=?8Sb)XBei`rcLa7RjJ8vI7_;qc zFkURA7jf}IV{tNGj$h!9U?rd9`uMq!1|DuUs))bHdaHdOI|AHq9G#?mt`D=Gx;2PQ z4`+Yk0g%O`adC+S=}49i;Thruz7yMwTIhcTO1U-GD)KC&Ze&?3s@69<10M}^mtpv$ z!k73U{!ID-c-U0Z@hfpVWJdeavx77z+32klj}8IupNO^6By)yy(SL<@GdH<)fYdL6 z(XgPDPYQz_FNK!~Te!pYCNjhwW7boH=)lPAh!d@<(#C50l$RHsp>uFI`O#t~Y~#vO z4QT*)Sd(#S(c4dEwqOqRZKg4r>|M7i zYJFnsBTFK4qovj7I<_Z!8^{#83%5p?DW?6y#jAKU^!6}3NkqaF_86u3m+WbJDaDGc zjwDCRE8DcAW+``HkdI!=Nsi;&@dJFRP1^z==z|E#p$!W(lh;k}=Au=mk zK`EzmRvC9$@SK{)UgXDt6?l&yfqq_s*3HHYJ}x}xU{(c5@a~&Pn*=M^`$wv!0HOxd5&(+ zFT!KxT%j>CEfocnISdFuG03J?fo$A#pL2dU+N)J!E28~lYt%AEKD&bF1!d`u+&&>5 ze+@Wg7Ac#QOS*s?LL4(4s0LD%f)36*V}syGebd zr_uze35_u{NC^r6bqSX!*V@`hTV|?OFv=r3Dxl^sg^uRt|VLF@w|sv>!mQ2 zKg6s?PrQ)bTd${{j-80zR|aVbW`3uSKMnl`V`m#t1{;uDz6rYdGv1H8iXVg`d`o5l zTJPPoj_RG%M#{a|A!W5z#cb-l^t(~p*p|X)(kppH=!ej1`I0mWuNT|G{dH%0qud~g z_lz_xQQe^|Qo5*VdO0iAtxA5UEAb)RKz<&o96lC$BoC7sfp^-G&%suss9>5i-K?(< zRL?8Zl@CfQ7|AL-g@Vj<8Lp3bR9X>YGBpUl4|S8XOW%uUz|WaOl?r(Gv>DdBshyQv zN^j+``ccnjAM~oCTx=CVmL>qk)*CRT7oiohC?$$D`FxP?Nbp3PH6E##l_5%YWv==} zyJ{x7hk~i}S#F=$R(3;OGG)osDQw7drHgRc%{%t<`iEC zPmsripN5+PPF6d#O}Y+qOAf+{rul9YtFeAu-KtDb`YYe4ceE693-nVxssh_y7>phH zLFkuonebFV+S33D&0sd5yI!*0!zid7RK8aB$9h3OwbwgYlqZuj^k8nXI6+Dbtqna2 zl?n}#X92RApF2wVzU)jgEVa0@Al59lG`3%nWD%76l8>a$|*U_<@`pS{N!6$}B&}7lqbb z9-8<$ow3Ftb#+XM&WgszqOl5^Zq#(>ATQN{O%&GPJAj2Ig`b4-KrSp1=M*ZjXVG%+ zC##fxQOOru8vQBSIF?1-t_RjP5Y=k*J$S^-U1?9KT6l9fZ#X&hQp%5e^YxfgB#T?d ztgUrb^2AO?pGTW2J%M|hooT@pY74tVAo!ZBgkFXY0$Q~m;)mhFT9%@^_%&=-->t@C z>te~V2Fe@QqvV9$$zP17Kpb{Vd?qywO$;%i?(!E>P22%S5t=>|>~bcUSG0A?$k>x; z!Pq?Iq?T?j2X8rvZp}3lCrUd*O~Q9WO+&hrAPo`I_(#lUqH)y)$c$e3M#GCY{pY-xp#qFr9JMZ zxLM8;-Wa|gN(vd$JHS7i@w1uZFiWwu{j;%9%cCYJS(VL7ORb#(3>}b&=9a;nha^O>=~s8oZ|Nuy2IbQjbvG z@a#}>=$Z5v9wHv%OR^8qD!;iy%)L5ML$LSuRHdc5Rv%~O_c*eXuEqDjY-m;Zt4wL( zC86GOKB>Q$z@KBX!5Svb-e<1W^Jw3xOB6#XrEN9p*ay4<=qy?6>23H zk+zB#`Ms<|y$|}jm#xzVt?OzN^@vhm9iUw`7TcfuDjLNe7rvFYhjxY+g;$4CWDjyJ zXZZ5$&#*eEr+eSpYfRV2Yp2vT>O8fGUfs;$9Q3PD5s;+*Qr-{~-V>S(=oAf6*FG+d zIfOR*kDUqDSfi0XUu&*iQmbiGVGO3>p;`s#=|C);r`ByB>q#`EB<0@8JGtOwQ z@6d*8m$Vs1X=|ptBbZAs;PQ!CrM&WCd71neM$&FD`g$zG97N56+wknJ2j(Gop2x3x zCH;!N(b!|9fTfk`MqD4E6Mi6Jxr)3?dWD<7oQgu+A*L@?mr(v<=N;6x(#T}o)YJ93 zMx3?A+2yyOs<7$&De*G4r1jD?X%?<4&gH-1+A(LTYh;#R93*O-x!8E1|E2$^TY7f0 zx&78XM%L2{x#q&Xf1COC7f@66xf2_y52_C^zPk5$O+ z7M!D6vVC|Ad85DZ5qt}8fc(c4K0o&hQ-Xd?PW$EElJ+L^qH)Oh(Ku?bW(n(-ec#K3 zKG0*hoPsL+DOLtl+Y)`@F+Z96n*B)Ep%6*+1ZRMS%)`bSqmOaksAyiaO1f2o=~NT8 zJAX>p4TxD^+#Bx`rwg6=f!uv&1pO3s2^xBNoK3I~el4@0S-|XX7PXL*%}XJp=s($k z{0yO-I1A9Wkzyv0-=kcAwmOrWK1lZaS>5+mx_QzVV3arV8gq^FW=?0EcZKw(->{YV zfx<-ammZ3v#M#10ek}JR8)9NeB&)m*&Sd=F2>z9@Q9O>v zeCr@9#|Y2%348T^?llp8lOaP_JzmfL9290=iD;;g0eXfRjt{4}dRK z0lIbk2Cg29XqDvmH#_~UXU5;UtdG=QYUK^jw4BTSSyY&bvCsJy;t0U@)`@=!al%@D zIjlj=&3I^DKzS$ZRc14zy8g9RO?#lNGXAnIyBmV))Key%yCEDG$KxJ=KyCt@Z@#dc zkFg2NG*mR`;y$w^vzxwOLt1&QgRU6E?O|R)a*Ya@4!i`}nS*#LE{6AuHN{#&Hhu#8 znp#F4dc&MzRwd)1mPH$_x@uQ_oSD_}yh~&r_=Y(I9`Aw7>qN*fY!WMry@Zy01vV4i zpRD&PI}gk=dMhnOeXNvI8U3Yk)jsJxARTCfEhoT+w-958q<**>UM&70+~k+CU(%h( zTyK|M$?TzjR9}G~m8eeB?id$r%Ezb>V{lLn)((_r3+yg-((CrcW;%CxT`HDOPDF4J~AYW62KfzR^Rs`R=Lt)pq1KKvV zi`r8?rasmV8%6APo<;I8FS+|dZ(LOR1AO$JQafA?Vxu##DkO#0NvglY`O&IiywEu9 zxcap=N&8!0V^(n%`ZtlmXa3{lo@>18eiXWA;)8Ip|ZaWxX^G=&f`| z8?P5IvYI9A>+Y&RgIt9qq>A044us|qFgFS#u21-c|?{37p1XRS5LTy1nU zXtR>p)mmYncY6o0-wRuZ-yv*)3?hX)0}}li_|Lof^RQOw6w`#J&}Dz7+sJ-oW;fRu z+l@-5YuZ+A_q~@JRi^i{h57S*O<@Ae$2lT25nR3%e-+kQk7pw^q8IirFx*2rY8G}l<3z1yu5j7A&jpV^~aoX|r29HQjGcrCc9AN9dT7W0Cc*?!}6@b{DN>6^?T?kz8f*TI?>#Vr7d?7x3kfm>Nh5Ns2g-e_5_S& z)5Mr~31a`Ec)0jhc*n={i(uz~veY6n0VK4!{hN8&*l1)o_nQ-~!uCfy(OuvdCUYs3 zzRWUwypS2}@Ds6)2-zXQKW-36!GGn+E!*5pQ6SVv$OTR{grdVUETR{OpE#SmHYAa{MO#dNt_Ytb>=%5Pl~-GM&(G)gb644E3`LhgbAol=LFc5k$82os z#%lAFnPiQ$nuDI`-h9xLKk4#pJeSDR!YHAQ@E1RnPv=I!xHW~zMC+&+`P=W{wRf&t zugpSbVRN#1&1`MiR!1khcgsIR(x_rgUUnt-C0|a+A(RyU;B)gC+<0y_JCQ-Og$|Ro zK_UODyW4(a<+AEor>ze5Ub~6Y&fV&j3ozg}IwtTRxrN!!O}m@b&mMTmok? zZ|FPJYjlfT4qR`B8|PqqzV*Oj?Vs&@&MW7=i~Xg+Z88C^r3TS87#Y?qn9!11TnhI! z*q1HrM)omNmpMeApvt3)WTL;*o$oxi4f~m$)9vQ6-Vv|0pEKATm(&H`Psb1~`+PKb=wT6nC@R+FRq*^-Bj= zgS+Gg%1_^?uQ2&o%+_R!vt`&U>?39z^9xPWnW#JpkouCjYOS+T%a+CBStw|6(56Y7&WG%T)zCkn4VPv7KR1qpf$y5`n6E%%m zL5+k@NmL=AKZ6D%0lg!eNeW>|9QhJz9Y&@Qg}f&zXe>H`9CQTDLmkj;G#c$jL*a_w zp|4OGR2CIP60*oDGK91xEy-|_N{*6EP(G4+=x3<6Fx1uz>M2a!g8N;FdZUuig3EB9 z*T^mMnw%v2$zC##tRuU~QnG=ZBAL+_=o{1$jYQL-){RiMpxYI7EJDg#FbD7{hLSXwgs5K5am<;#N z!TE#a1^JtJz~3UkgB-{qBC3O`qBf`ow6PFes{~vz5q$>q1%av@l(JA)Zj=K71Patg z;n+>KlgZGRQ=mN?;d&O_WmWjyVAKq?hhq}z1^noWssSaB9+2DQAhdWPysscffCItBOib^JG8VcaHI)b zp(k*$C2EB#qe{@$&w#sx*yJ75aU1&V9JvJLI()iE?vrQG(=Xw_9)Swr#UT;+B*A+K zVc@v{2SP6Nn?*FJDMoZ+5dWX=9?-#nN65cu5ds$-4(gvPQUCn?|DXRk@ju?0H)2@n zpYLY-GY!|o(sm`$2z@FXoGqO=;@jaZzo}R63pmlPX4|^G2Ba3q0{{4Q$*d(2D%2K1 zHCfPi-===c!f$RNhB+n-4M%dc%3r1MiiBqcw&xtiw~2MT*Zs_#eSNmh-}3U;`suJ0 z5B;&|wfYUe9XF`(VCmb-b>8JKp=SQ8?`D5qkvSPlD;{|B@L6b8r8?aQ4?i$!UGKl^ zrsQh)qW^)>^B+y>H*Lt8x#tRc&yvP32Ja zP?hH0B7<`EY1>pv7@~AYPg#F(!N_IS-ro1tHh?McVu54JhJk@r&YgRp;E$We*Wvcm#6(9p2%^f#oK=3_#y==i8T*Y zSX{Uj|J9qeS4y0EcdFwx?%g-S!a{e-U9Iw=T*Ja0as7ITpm)F0^K+Le zF*0de$tw9Z@jLa8r@!4XZtr-UC31->p1pJYmck9`K0d@l*5sm3b`nLMit;{B+vW(Qi_;U+7X~vDr2$sP`_ixlesX>F_-kC*oOT!Xx@mTB@kQ$qWejxoFY!c zov>hOvxq>H|NAv~=%Bve_e@RfJ8aOve^a7=DEC~;_B_HO^r?Ij$A7=>!1a<62MJM(9)dPm;G#ed)wu!~Yk__#dt0mY*T?sr)C2SqP!z|BGZ~$-&#Aa8sYk zf0FdCj8K{X>&9xvUal-PAE8g>KS?Ik_|z6qwEu3)e{18vn}7NGzjU*CBaZ)c`jc_! M4;o(Z)~9d(55ztL+yDRo literal 0 HcmV?d00001 diff --git a/test/integration/clone-cleanup.js b/test/integration/clone-cleanup.js new file mode 100644 index 00000000..bfecd6d7 --- /dev/null +++ b/test/integration/clone-cleanup.js @@ -0,0 +1,96 @@ +const path = require('path'); +const test = require('tap').test; +const attachTestStorage = require('../fixtures/attach-test-storage'); +const extract = require('../fixtures/extract'); +const VirtualMachine = require('../../src/index'); + +const projectUri = path.resolve(__dirname, '../fixtures/clone-cleanup.sb2'); +const project = extract(projectUri); + +test('clone-cleanup', t => { + const vm = new VirtualMachine(); + attachTestStorage(vm); + + /** + * Track which step of the project is currently under test. + * @type {number} + */ + let testStep = -1; + + /** + * We test using setInterval; track the interval ID here so we can cancel it. + * @type {object} + */ + let testInterval = null; + + const verifyCounts = (expectedClones, extraThreads) => { + // stage plus one sprite, plus clones + t.strictEqual(vm.runtime.targets.length, 2 + expectedClones, + `target count at step ${testStep}`); + + // the stage should never have any clones + t.strictEqual(vm.runtime.targets[0].sprite.clones.length, 1, + `stage clone count at step ${testStep}`); + + // check sprite clone count (+1 for original) + t.strictEqual(vm.runtime.targets[1].sprite.clones.length, 1 + expectedClones, + `sprite clone count at step ${testStep}`); + + // thread count isn't directly tied to clone count since threads can end + t.strictEqual(vm.runtime.threads.length, extraThreads + (2 * expectedClones), + `thread count at step ${testStep}`); + }; + + const testNextStep = () => { + ++testStep; + switch (testStep) { + case 0: + // Project has started, main thread running, no clones yet + verifyCounts(0, 1); + break; + + case 1: + // 10 clones have been created, main thread still running + verifyCounts(10, 1); + break; + + case 2: + // The first batch of clones has deleted themselves; main thread still running + verifyCounts(0, 1); + break; + + case 3: + // The second batch of clones has been created and the main thread has ended + verifyCounts(10, 0); + break; + + case 4: + // The second batch of clones has deleted themselves; everything is finished + verifyCounts(0, 0); + + clearInterval(testInterval); + t.end(); + process.nextTick(process.exit); + break; + } + }; + + // Start VM, load project, and run + t.doesNotThrow(() => { + vm.start(); + vm.clear(); + vm.setCompatibilityMode(false); + vm.setTurboMode(false); + vm.loadProject(project).then(() => { + + // Verify initial state: no clones, nothing running ("step -1") + verifyCounts(0, 0); + + vm.greenFlag(); + + // Every second, advance the testing step + testInterval = setInterval(testNextStep, 1000); + }); + }); + +}); From 9c5d43e9d3f6da2c75cccc851756b7b355244cb4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 12 Jun 2017 08:35:27 -0400 Subject: [PATCH 384/721] Enhance sprite delete behavior --- src/virtual-machine.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 436ff60e..12e6e7a7 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -364,6 +364,8 @@ class VirtualMachine extends EventEmitter { */ deleteSprite (targetId) { const target = this.runtime.getTargetById(targetId); + const targetIndexBeforeDelete = this.runtime.targets.map(t => t.id).indexOf(target.id); + if (target) { if (!target.isSprite()) { throw new Error('Cannot delete non-sprite targets.'); @@ -379,8 +381,8 @@ class VirtualMachine extends EventEmitter { this.runtime.disposeTarget(sprite.clones[i]); // Ensure editing target is switched if we are deleting it. if (clone === currentEditingTarget) { - const lastTargetIndex = this.runtime.targets.length - 1; - this.setEditingTarget(this.runtime.targets[lastTargetIndex].id); + const nextTargetIndex = Math.min(this.runtime.targets.length - 1, targetIndexBeforeDelete); + this.setEditingTarget(this.runtime.targets[nextTargetIndex].id); } } // Sprite object should be deleted by GC. From 88e44c65eb65b9d39cf747630627bdb32ab49026 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Thu, 8 Jun 2017 12:47:56 -0700 Subject: [PATCH 385/721] Remember the data format when an asset is imported --- src/import/load-costume.js | 2 +- src/import/load-sound.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/import/load-costume.js b/src/import/load-costume.js index 54962ea1..d435129b 100644 --- a/src/import/load-costume.js +++ b/src/import/load-costume.js @@ -32,7 +32,7 @@ const loadCostume = function (md5ext, costume, runtime) { let promise = runtime.storage.load(assetType, md5, ext).then(costumeAsset => { costume.assetId = costumeAsset.assetId; - costume.assetType = assetType; + costume.dataFormat = ext; return costumeAsset; }); diff --git a/src/import/load-sound.js b/src/import/load-sound.js index 80b59598..295b87eb 100644 --- a/src/import/load-sound.js +++ b/src/import/load-sound.js @@ -24,7 +24,7 @@ const loadSound = function (sound, runtime) { return runtime.storage.load(runtime.storage.AssetType.Sound, md5, ext) .then(soundAsset => { sound.assetId = soundAsset.assetId; - sound.assetType = runtime.storage.AssetType.Sound; + sound.dataFormat = ext; return runtime.audioEngine.decodeSound(Object.assign( {}, sound, From cd1c72c7cc3610710cb0cd4ce40f5284652411f2 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 9 Jun 2017 14:54:10 -0700 Subject: [PATCH 386/721] Convert `attachTestStorage` to `makeTestStorage` Rather than assuming that the storage instance will be attached to a VM, just return it. Callers may attach it to a `VM` or (in the case of `import_sb2.js`) to a `Runtime`. --- .../{attach-test-storage.js => make-test-storage.js} | 10 +++++----- test/integration/clone-cleanup.js | 4 ++-- test/integration/complex.js | 4 ++-- test/integration/control.js | 4 ++-- test/integration/data.js | 4 ++-- test/integration/event.js | 4 ++-- test/integration/hat-execution-order.js | 4 ++-- test/integration/import_sb2.js | 4 ++-- test/integration/looks.js | 4 ++-- test/integration/motion.js | 4 ++-- test/integration/pen.js | 4 ++-- test/integration/procedure.js | 4 ++-- test/integration/sensing.js | 4 ++-- test/integration/sound.js | 4 ++-- 14 files changed, 31 insertions(+), 31 deletions(-) rename test/fixtures/{attach-test-storage.js => make-test-storage.js} (79%) diff --git a/test/fixtures/attach-test-storage.js b/test/fixtures/make-test-storage.js similarity index 79% rename from test/fixtures/attach-test-storage.js rename to test/fixtures/make-test-storage.js index bb0cc3a0..0179ca34 100644 --- a/test/fixtures/attach-test-storage.js +++ b/test/fixtures/make-test-storage.js @@ -33,15 +33,15 @@ const getAssetUrl = function (asset) { }; /** - * Construct a new instance of ScratchStorage, provide it with default web sources, and attach it to the provided VM. - * @param {VirtualMachine} vm - the VM which will own the new ScratchStorage instance. + * Construct a new instance of ScratchStorage and provide it with default web sources. + * @returns {ScratchStorage} - an instance of ScratchStorage, ready to be used for tests. */ -const attachTestStorage = function (vm) { +const makeTestStorage = function () { const storage = new ScratchStorage(); const AssetType = storage.AssetType; storage.addWebSource([AssetType.Project], getProjectUrl); storage.addWebSource([AssetType.ImageVector, AssetType.ImageBitmap, AssetType.Sound], getAssetUrl); - vm.attachStorage(storage); + return storage; }; -module.exports = attachTestStorage; +module.exports = makeTestStorage; diff --git a/test/integration/clone-cleanup.js b/test/integration/clone-cleanup.js index bfecd6d7..8b801a00 100644 --- a/test/integration/clone-cleanup.js +++ b/test/integration/clone-cleanup.js @@ -1,6 +1,6 @@ const path = require('path'); const test = require('tap').test; -const attachTestStorage = require('../fixtures/attach-test-storage'); +const makeTestStorage = require('../fixtures/make-test-storage'); const extract = require('../fixtures/extract'); const VirtualMachine = require('../../src/index'); @@ -9,7 +9,7 @@ const project = extract(projectUri); test('clone-cleanup', t => { const vm = new VirtualMachine(); - attachTestStorage(vm); + vm.attachStorage(makeTestStorage()); /** * Track which step of the project is currently under test. diff --git a/test/integration/complex.js b/test/integration/complex.js index 39bc0c66..11392848 100644 --- a/test/integration/complex.js +++ b/test/integration/complex.js @@ -1,7 +1,7 @@ const fs = require('fs'); const path = require('path'); const test = require('tap').test; -const attachTestStorage = require('../fixtures/attach-test-storage'); +const makeTestStorage = require('../fixtures/make-test-storage'); const extract = require('../fixtures/extract'); const VirtualMachine = require('../../src/index'); @@ -13,7 +13,7 @@ const sprite = fs.readFileSync(spriteUri, 'utf8'); test('complex', t => { const vm = new VirtualMachine(); - attachTestStorage(vm); + vm.attachStorage(makeTestStorage()); // Evaluate playground data and exit vm.on('playgroundData', e => { diff --git a/test/integration/control.js b/test/integration/control.js index 4b65a016..66b4c028 100644 --- a/test/integration/control.js +++ b/test/integration/control.js @@ -1,6 +1,6 @@ const path = require('path'); const test = require('tap').test; -const attachTestStorage = require('../fixtures/attach-test-storage'); +const makeTestStorage = require('../fixtures/make-test-storage'); const extract = require('../fixtures/extract'); const VirtualMachine = require('../../src/index'); @@ -9,7 +9,7 @@ const project = extract(uri); test('control', t => { const vm = new VirtualMachine(); - attachTestStorage(vm); + vm.attachStorage(makeTestStorage()); // Evaluate playground data and exit vm.on('playgroundData', e => { diff --git a/test/integration/data.js b/test/integration/data.js index ba263fec..c6cd71d1 100644 --- a/test/integration/data.js +++ b/test/integration/data.js @@ -1,6 +1,6 @@ const path = require('path'); const test = require('tap').test; -const attachTestStorage = require('../fixtures/attach-test-storage'); +const makeTestStorage = require('../fixtures/make-test-storage'); const extract = require('../fixtures/extract'); const VirtualMachine = require('../../src/index'); @@ -9,7 +9,7 @@ const project = extract(uri); test('data', t => { const vm = new VirtualMachine(); - attachTestStorage(vm); + vm.attachStorage(makeTestStorage()); // Evaluate playground data and exit vm.on('playgroundData', () => { diff --git a/test/integration/event.js b/test/integration/event.js index 34e21c63..c4f3ee58 100644 --- a/test/integration/event.js +++ b/test/integration/event.js @@ -1,6 +1,6 @@ const path = require('path'); const test = require('tap').test; -const attachTestStorage = require('../fixtures/attach-test-storage'); +const makeTestStorage = require('../fixtures/make-test-storage'); const extract = require('../fixtures/extract'); const VirtualMachine = require('../../src/index'); @@ -9,7 +9,7 @@ const project = extract(uri); test('event', t => { const vm = new VirtualMachine(); - attachTestStorage(vm); + vm.attachStorage(makeTestStorage()); // Evaluate playground data and exit vm.on('playgroundData', e => { diff --git a/test/integration/hat-execution-order.js b/test/integration/hat-execution-order.js index 44efcfa0..c8d2dcab 100644 --- a/test/integration/hat-execution-order.js +++ b/test/integration/hat-execution-order.js @@ -1,6 +1,6 @@ const path = require('path'); const test = require('tap').test; -const attachTestStorage = require('../fixtures/attach-test-storage'); +const makeTestStorage = require('../fixtures/make-test-storage'); const extract = require('../fixtures/extract'); const VirtualMachine = require('../../src/index'); @@ -9,7 +9,7 @@ const project = extract(projectUri); test('complex', t => { const vm = new VirtualMachine(); - attachTestStorage(vm); + vm.attachStorage(makeTestStorage()); // Evaluate playground data and exit vm.on('playgroundData', e => { diff --git a/test/integration/import_sb2.js b/test/integration/import_sb2.js index 3b375a44..f9a0d2d0 100644 --- a/test/integration/import_sb2.js +++ b/test/integration/import_sb2.js @@ -1,6 +1,6 @@ const path = require('path'); const test = require('tap').test; -const attachTestStorage = require('../fixtures/attach-test-storage'); +const makeTestStorage = require('../fixtures/make-test-storage'); const extract = require('../fixtures/extract'); const renderedTarget = require('../../src/sprites/rendered-target'); @@ -20,7 +20,7 @@ test('default', t => { // Create runtime instance & load SB2 into it const rt = new runtime(); - attachTestStorage(rt); + rt.attachStorage(makeTestStorage()); sb2.deserialize(json, rt).then(targets => { // Test t.type(file, 'string'); diff --git a/test/integration/looks.js b/test/integration/looks.js index d251d4f8..87464649 100644 --- a/test/integration/looks.js +++ b/test/integration/looks.js @@ -1,6 +1,6 @@ const path = require('path'); const test = require('tap').test; -const attachTestStorage = require('../fixtures/attach-test-storage'); +const makeTestStorage = require('../fixtures/make-test-storage'); const extract = require('../fixtures/extract'); const VirtualMachine = require('../../src/index'); @@ -9,7 +9,7 @@ const project = extract(uri); test('looks', t => { const vm = new VirtualMachine(); - attachTestStorage(vm); + vm.attachStorage(makeTestStorage()); // Evaluate playground data and exit vm.on('playgroundData', e => { diff --git a/test/integration/motion.js b/test/integration/motion.js index d8b1fc8d..689b2187 100644 --- a/test/integration/motion.js +++ b/test/integration/motion.js @@ -1,6 +1,6 @@ const path = require('path'); const test = require('tap').test; -const attachTestStorage = require('../fixtures/attach-test-storage'); +const makeTestStorage = require('../fixtures/make-test-storage'); const extract = require('../fixtures/extract'); const VirtualMachine = require('../../src/index'); @@ -9,7 +9,7 @@ const project = extract(uri); test('motion', t => { const vm = new VirtualMachine(); - attachTestStorage(vm); + vm.attachStorage(makeTestStorage()); // Evaluate playground data and exit vm.on('playgroundData', e => { diff --git a/test/integration/pen.js b/test/integration/pen.js index f4da55fd..24d86e95 100644 --- a/test/integration/pen.js +++ b/test/integration/pen.js @@ -1,6 +1,6 @@ const path = require('path'); const test = require('tap').test; -const attachTestStorage = require('../fixtures/attach-test-storage'); +const makeTestStorage = require('../fixtures/make-test-storage'); const extract = require('../fixtures/extract'); const VirtualMachine = require('../../src/index'); @@ -9,7 +9,7 @@ const project = extract(uri); test('pen', t => { const vm = new VirtualMachine(); - attachTestStorage(vm); + vm.attachStorage(makeTestStorage()); // Evaluate playground data and exit vm.on('playgroundData', () => { diff --git a/test/integration/procedure.js b/test/integration/procedure.js index 4abcf1e4..e62f2b10 100644 --- a/test/integration/procedure.js +++ b/test/integration/procedure.js @@ -1,6 +1,6 @@ const path = require('path'); const test = require('tap').test; -const attachTestStorage = require('../fixtures/attach-test-storage'); +const makeTestStorage = require('../fixtures/make-test-storage'); const extract = require('../fixtures/extract'); const VirtualMachine = require('../../src/index'); @@ -9,7 +9,7 @@ const project = extract(uri); test('procedure', t => { const vm = new VirtualMachine(); - attachTestStorage(vm); + vm.attachStorage(makeTestStorage()); // Evaluate playground data and exit vm.on('playgroundData', e => { diff --git a/test/integration/sensing.js b/test/integration/sensing.js index ca8fa1bb..1d7d3085 100644 --- a/test/integration/sensing.js +++ b/test/integration/sensing.js @@ -1,6 +1,6 @@ const path = require('path'); const test = require('tap').test; -const attachTestStorage = require('../fixtures/attach-test-storage'); +const makeTestStorage = require('../fixtures/make-test-storage'); const extract = require('../fixtures/extract'); const VirtualMachine = require('../../src/index'); @@ -9,7 +9,7 @@ const project = extract(uri); test('sensing', t => { const vm = new VirtualMachine(); - attachTestStorage(vm); + vm.attachStorage(makeTestStorage()); // Evaluate playground data and exit vm.on('playgroundData', e => { diff --git a/test/integration/sound.js b/test/integration/sound.js index 7ab10159..50100d70 100644 --- a/test/integration/sound.js +++ b/test/integration/sound.js @@ -1,6 +1,6 @@ const path = require('path'); const test = require('tap').test; -const attachTestStorage = require('../fixtures/attach-test-storage'); +const makeTestStorage = require('../fixtures/make-test-storage'); const extract = require('../fixtures/extract'); const VirtualMachine = require('../../src/index'); @@ -9,7 +9,7 @@ const project = extract(uri); test('sound', t => { const vm = new VirtualMachine(); - attachTestStorage(vm); + vm.attachStorage(makeTestStorage()); // Evaluate playground data and exit vm.on('playgroundData', e => { From 4b1aad5099853c65d47a87743b8a438d51b8b9a0 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Mon, 12 Jun 2017 11:27:51 -0700 Subject: [PATCH 387/721] Add test for SB3 serialization round trip --- test/integration/sb3-roundtrip.js | 110 ++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 test/integration/sb3-roundtrip.js diff --git a/test/integration/sb3-roundtrip.js b/test/integration/sb3-roundtrip.js new file mode 100644 index 00000000..bc5da9d0 --- /dev/null +++ b/test/integration/sb3-roundtrip.js @@ -0,0 +1,110 @@ +const test = require('tap').test; + +const Blocks = require('../../src/engine/blocks'); +const Clone = require('../../src/util/clone'); +const loadCostume = require('../../src/import/load-costume'); +const loadSound = require('../../src/import/load-sound'); +const makeTestStorage = require('../fixtures/make-test-storage'); +const Runtime = require('../../src/engine/runtime'); +const sb3 = require('../../src/serialization/sb3'); +const Sprite = require('../../src/sprites/sprite'); + +const defaultCostumeInfo = { + bitmapResolution: 1, + rotationCenterX: 0, + rotationCenterY: 0 +}; + +const defaultSoundInfo = { +}; + +test('sb3-roundtrip', t => { + const runtime1 = new Runtime(); + runtime1.attachStorage(makeTestStorage()); + + const runtime2 = new Runtime(); + runtime2.attachStorage(makeTestStorage()); + + const testRuntimeState = (label, runtime) => { + t.strictEqual(runtime.targets.length, 2, `${label}: target count`); + const [stageClone, spriteClone] = runtime.targets; + + t.strictEqual(stageClone.isOriginal, true); + t.strictEqual(stageClone.isStage, true); + + const stage = stageClone.sprite; + t.strictEqual(stage.name, 'Stage'); + t.strictEqual(stage.clones.length, 1); + t.strictEqual(stage.clones[0], stageClone); + + t.strictEqual(stage.costumes.length, 1); + const [building] = stage.costumes; + t.strictEqual(building.assetId, 'fe5e3566965f9de793beeffce377d054'); + t.strictEqual(building.dataFormat, 'jpg'); + + t.strictEqual(stage.sounds.length, 0); + + t.strictEqual(spriteClone.isOriginal, true); + t.strictEqual(spriteClone.isStage, false); + + const sprite = spriteClone.sprite; + t.strictEqual(sprite.name, 'Sprite'); + t.strictEqual(sprite.clones.length, 1); + t.strictEqual(sprite.clones[0], spriteClone); + + t.strictEqual(sprite.costumes.length, 2); + const [cat, squirrel] = sprite.costumes; + t.strictEqual(cat.assetId, 'f88bf1935daea28f8ca098462a31dbb0'); + t.strictEqual(cat.dataFormat, 'svg'); + t.strictEqual(squirrel.assetId, '7e24c99c1b853e52f8e7f9004416fa34'); + t.strictEqual(squirrel.dataFormat, 'png'); + + t.strictEqual(sprite.sounds.length, 1); + const [meow] = sprite.sounds; + t.strictEqual(meow.md5, '83c36d806dc92327b9e7049a565c6bff.wav'); + }; + + const loadThings = Promise.all([ + loadCostume('fe5e3566965f9de793beeffce377d054.jpg', Clone.simple(defaultCostumeInfo), runtime1), + loadCostume('f88bf1935daea28f8ca098462a31dbb0.svg', Clone.simple(defaultCostumeInfo), runtime1), + loadCostume('7e24c99c1b853e52f8e7f9004416fa34.png', Clone.simple(defaultCostumeInfo), runtime1), + loadSound(Object.assign({md5: '83c36d806dc92327b9e7049a565c6bff.wav'}, defaultSoundInfo), runtime1) + ]); + + const installThings = loadThings.then(results => { + const [building, cat, squirrel, meow] = results; + + const stageBlocks = new Blocks(); + const stage = new Sprite(stageBlocks, runtime1); + stage.name = 'Stage'; + stage.costumes = [building]; + stage.sounds = []; + const stageClone = stage.createClone(); + stageClone.isStage = true; + + const spriteBlocks = new Blocks(); + const sprite = new Sprite(spriteBlocks, runtime1); + sprite.name = 'Sprite'; + sprite.costumes = [cat, squirrel]; + sprite.sounds = [meow]; + const spriteClone = sprite.createClone(); + + runtime1.targets = [stageClone, spriteClone]; + + testRuntimeState('original', runtime1); + }); + + const serializeAndDeserialize = installThings.then(() => { + // `Clone.simple` here helps in two ways: + // 1. it ensures that any non-serializable data is thrown away, and + // 2. `sb3.deserialize` does some `hasOwnProperty` checks which fail on the object returned by `sb3.serialize` + // due to its inheritance structure. + const serializedState = Clone.simple(sb3.serialize(runtime1)); + return sb3.deserialize(serializedState, runtime2); + }); + + return serializeAndDeserialize.then(targets => { + runtime2.targets = targets; + testRuntimeState('copy', runtime2); + }); +}); From 1bf180882654605b9c32001f07e5727d23b6c915 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Tue, 13 Jun 2017 13:58:34 -0700 Subject: [PATCH 388/721] Clarify "flattening" step in SB3 roundtrip test --- test/integration/sb3-roundtrip.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/integration/sb3-roundtrip.js b/test/integration/sb3-roundtrip.js index bc5da9d0..c401248b 100644 --- a/test/integration/sb3-roundtrip.js +++ b/test/integration/sb3-roundtrip.js @@ -95,11 +95,11 @@ test('sb3-roundtrip', t => { }); const serializeAndDeserialize = installThings.then(() => { - // `Clone.simple` here helps in two ways: + // Doing a JSON `stringify` and `parse` here more accurately simulate a save/load cycle. In particular: // 1. it ensures that any non-serializable data is thrown away, and - // 2. `sb3.deserialize` does some `hasOwnProperty` checks which fail on the object returned by `sb3.serialize` - // due to its inheritance structure. - const serializedState = Clone.simple(sb3.serialize(runtime1)); + // 2. `sb3.deserialize` and its helpers do some `hasOwnProperty` checks which fail on the object returned by + // `sb3.serialize` but succeed if that object is "flattened" in this way. + const serializedState = JSON.parse(JSON.stringify(sb3.serialize(runtime1))); return sb3.deserialize(serializedState, runtime2); }); From 884ba19aab57611da74bceeee09c04f98349f7df Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Tue, 13 Jun 2017 14:56:06 -0700 Subject: [PATCH 389/721] Add support for older SB3 JSON If the `dataFormat` field is absent, fall back on the older `assetType` field. That can at least help us tell if something is a vector or bitmap image, though it doesn't tell us which kind of bitmap. --- src/serialization/sb3.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 816b1af1..3ffac8ea 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -75,7 +75,11 @@ const parseScratchObject = function (object, runtime) { rotationCenterX: costumeSource.rotationCenterX, rotationCenterY: costumeSource.rotationCenterY }; - const costumeMd5 = `${costumeSource.assetId}.${costumeSource.dataFormat}`; + const dataFormat = + costumeSource.dataFormat || + (costumeSource.assetType && costumeSource.assetType.runtimeFormat) || // older format + 'png'; // if all else fails, guess that it might be a PNG + const costumeMd5 = `${costumeSource.assetId}.${dataFormat}`; return loadCostume(costumeMd5, costume, runtime); }); // Sounds from JSON From 5df4456c4aa91fb4afe05c491e3ade2d8628cd4e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 14 Jun 2017 16:31:29 -0400 Subject: [PATCH 390/721] Use variable field directly --- src/serialization/sb2_specmap.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/serialization/sb2_specmap.js b/src/serialization/sb2_specmap.js index 0f62a42f..8f574624 100644 --- a/src/serialization/sb2_specmap.js +++ b/src/serialization/sb2_specmap.js @@ -1191,9 +1191,8 @@ const specMap = { opcode: 'data_setvariableto', argMap: [ { - type: 'input', - inputOp: 'data_variablemenu', - inputName: 'VARIABLE' + type: 'field', + fieldName: 'VARIABLE' }, { type: 'input', @@ -1206,9 +1205,8 @@ const specMap = { opcode: 'data_changevariableby', argMap: [ { - type: 'input', - inputOp: 'data_variablemenu', - inputName: 'VARIABLE' + type: 'field', + fieldName: 'VARIABLE' }, { type: 'input', @@ -1221,9 +1219,8 @@ const specMap = { opcode: 'data_showvariable', argMap: [ { - type: 'input', - inputOp: 'data_variablemenu', - inputName: 'VARIABLE' + type: 'field', + fieldName: 'VARIABLE' } ] }, @@ -1231,9 +1228,8 @@ const specMap = { opcode: 'data_hidevariable', argMap: [ { - type: 'input', - inputOp: 'data_variablemenu', - inputName: 'VARIABLE' + type: 'field', + fieldName: 'VARIABLE' } ] }, From f9708b20462b4d16cfa57373127e2c6efb38e5f0 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 15 Jun 2017 11:24:58 -0400 Subject: [PATCH 391/721] Update README for current process The README has gotten a bit out of date and inaccurate. Clarify how to work with scratch-vm in the current state of the project. --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2272e5da..6bd31f90 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ ## Installation This requires you to have Git and Node.js installed. -In your own node environment/application: +To install as a dependency for your own application: ```bash -npm install https://github.com/LLK/scratch-vm.git +npm install scratch-vm ``` -If you want to edit/play yourself: +To set up a development environment to edit scratch-vm yourself: ```bash git clone https://github.com/LLK/scratch-vm.git cd scratch-vm @@ -22,7 +22,7 @@ npm install ## Development Server This requires Node.js to be installed. -For convenience, we've included a development server with the VM. This is sometimes useful when running in an environment that's loading remote resources (e.g., SVGs from the Scratch server). +For convenience, we've included a development server with the VM. This is sometimes useful when running in an environment that's loading remote resources (e.g., SVGs from the Scratch server). If you would like to use your modified VM with the full Scratch 3.0 GUI, [follow the instructions to link the VM to the GUI](https://github.com/LLK/scratch-gui/wiki/Getting-Started). ## Running the Development Server Open a Command Prompt or Terminal in the repository and run: @@ -31,7 +31,7 @@ npm start ``` ## Playground -To run the Playground, make sure the dev server's running and go to [http://localhost:8073/playground/](http://localhost:8073/playground/) - you will be directed to the playground, which demonstrates various tools and internal state. +To view the Playground, make sure the dev server's running and go to [http://localhost:8073/playground/](http://localhost:8073/playground/) - you will be directed to the playground, which demonstrates various tools and internal state. ![VM Playground Screenshot](https://i.imgur.com/nOCNqEc.gif) @@ -42,7 +42,7 @@ npm run build ``` ```html - + - + + + diff --git a/src/playground/playground.js b/src/playground/playground.js index 2c1ef330..321afb15 100644 --- a/src/playground/playground.js +++ b/src/playground/playground.js @@ -46,7 +46,7 @@ window.onload = function () { const vm = new window.VirtualMachine(); Scratch.vm = vm; - const storage = new Scratch.Storage(); + const storage = new ScratchStorage(); /* global ScratchStorage */ const AssetType = storage.AssetType; storage.addWebSource([AssetType.Project], getProjectUrl); storage.addWebSource([AssetType.ImageVector, AssetType.ImageBitmap, AssetType.Sound], getAssetUrl); diff --git a/webpack.config.js b/webpack.config.js index 57125680..b58666d4 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -77,9 +77,7 @@ module.exports = [ // Audio 'scratch-audio', // Renderer - 'scratch-render', - // Storage - 'scratch-storage' + 'scratch-render' ] }, output: { @@ -111,10 +109,6 @@ module.exports = [ { test: require.resolve('scratch-render'), loader: 'expose-loader?RenderWebGL' - }, - { - test: require.resolve('scratch-storage'), - loader: 'expose-loader?Scratch.Storage' } ]) }, @@ -124,6 +118,8 @@ module.exports = [ to: 'media' }, { from: 'node_modules/highlightjs/styles/zenburn.css' + }, { + from: 'node_modules/scratch-storage/dist/web' }, { from: 'src/playground' }]) From 240c48ad0c1ef4c3e6de837eb106330ca03d6152 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 17 Oct 2017 19:31:51 -0400 Subject: [PATCH 509/721] Set or change color param with a single function --- src/blocks/scratch3_pen.js | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/src/blocks/scratch3_pen.js b/src/blocks/scratch3_pen.js index 01891815..e37765df 100644 --- a/src/blocks/scratch3_pen.js +++ b/src/blocks/scratch3_pen.js @@ -415,42 +415,32 @@ class Scratch3PenBlocks { penState.penAttributes.color4f[3] = this._transparencyToAlpha(penState.transparency); } - changePenColorParamBy (args, util) { - const penState = this._getPenState(util.target); - switch (args.COLOR_PARAM) { + _setOrChangeColorParam (param, value, penState, change) { + switch (param) { case ColorParam.COLOR: - penState.color = this._wrapColor(penState.color + Cast.toNumber(args.VALUE)); + penState.color = this._wrapColor(value + (change ? penState.color : 0)); break; case ColorParam.SATURATION: - penState.saturation = this._clampColorParam(penState.saturation + Cast.toNumber(args.VALUE)); + penState.saturation = this._clampColorParam(value + (change ? penState.saturation : 0)); break; case ColorParam.BRIGHTNESS: - penState.brightness = this._clampColorParam(penState.brightness + Cast.toNumber(args.VALUE)); + penState.brightness = this._clampColorParam(value + (change ? penState.brightness : 0)); break; case ColorParam.TRANSPARENCY: - penState.transparency = this._clampColorParam(penState.transparency + Cast.toNumber(args.VALUE)); + penState.transparency = this._clampColorParam(value + (change ? penState.transparency : 0)); break; } this._updatePenColor(penState); } + changePenColorParamBy (args, util) { + const penState = this._getPenState(util.target); + this._setOrChangeColorParam(args.COLOR_PARAM, Cast.toNumber(args.VALUE), penState, true); + } + setPenColorParamTo (args, util) { const penState = this._getPenState(util.target); - switch (args.COLOR_PARAM) { - case ColorParam.COLOR: - penState.color = this._wrapColor(Cast.toNumber(args.VALUE)); - break; - case ColorParam.SATURATION: - penState.saturation = this._clampColorParam(Cast.toNumber(args.VALUE)); - break; - case ColorParam.BRIGHTNESS: - penState.brightness = this._clampColorParam(Cast.toNumber(args.VALUE)); - break; - case ColorParam.TRANSPARENCY: - penState.transparency = this._clampColorParam(Cast.toNumber(args.VALUE)); - break; - } - this._updatePenColor(penState); + this._setOrChangeColorParam(args.COLOR_PARAM, Cast.toNumber(args.VALUE), penState, false); } /** From a32f7a6402052a7fe22eb9d5f7d50964e6e55815 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 17 Oct 2017 23:10:58 -0400 Subject: [PATCH 510/721] Default color setting matches default RGB values For color (hue) in range 0-100, to get RGB (0, 0, 1), we need to use 100 * 2/3 = 66.66 --- src/blocks/scratch3_pen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/scratch3_pen.js b/src/blocks/scratch3_pen.js index e37765df..84aad4c5 100644 --- a/src/blocks/scratch3_pen.js +++ b/src/blocks/scratch3_pen.js @@ -67,7 +67,7 @@ class Scratch3PenBlocks { static get DEFAULT_PEN_STATE () { return { penDown: false, - color: 33, + color: 66.66, saturation: 100, brightness: 100, transparency: 0, From aab1b834d6b82e4461937405f1ebb1bb909f8739 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 17 Oct 2017 23:13:05 -0400 Subject: [PATCH 511/721] On set color using picker, reset transparency to 0 --- src/blocks/scratch3_pen.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/blocks/scratch3_pen.js b/src/blocks/scratch3_pen.js index 84aad4c5..ed120409 100644 --- a/src/blocks/scratch3_pen.js +++ b/src/blocks/scratch3_pen.js @@ -383,6 +383,7 @@ class Scratch3PenBlocks { /** * The pen "set pen color to {color}" block sets the pen to a particular RGB color. + * The transparency is reset to 0. * @param {object} args - the block arguments. * @property {int} COLOR - the color to set, expressed as a 24-bit RGB value (0xRRGGBB). * @param {object} util - utility object provided by the runtime. @@ -394,6 +395,7 @@ class Scratch3PenBlocks { penState.color = (hsv.h / 360) * 100; penState.saturation = hsv.s * 100; penState.brightness = hsv.v * 100; + penState.transparency = 0; this._updatePenColor(penState); } From b5ad5e1f6b88575eb2ef5269c5ffc9385c32932b Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 18 Oct 2017 11:33:25 -0400 Subject: [PATCH 512/721] Warn on set or change unknown color parameter --- src/blocks/scratch3_pen.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/blocks/scratch3_pen.js b/src/blocks/scratch3_pen.js index ed120409..7eff46a0 100644 --- a/src/blocks/scratch3_pen.js +++ b/src/blocks/scratch3_pen.js @@ -5,7 +5,7 @@ const Clone = require('../util/clone'); const Color = require('../util/color'); const MathUtil = require('../util/math-util'); const RenderedTarget = require('../sprites/rendered-target'); - +const log = require('../util/log'); /** * Enum for pen color parameters. @@ -431,6 +431,9 @@ class Scratch3PenBlocks { case ColorParam.TRANSPARENCY: penState.transparency = this._clampColorParam(value + (change ? penState.transparency : 0)); break; + default: + log.warn(`Tried to set or change unknown color parameter: ${param}`); + } this._updatePenColor(penState); } From bddabdbac6cbe4a08ccd8b43bc52493f8ee2f7e5 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 18 Oct 2017 11:48:45 -0400 Subject: [PATCH 513/721] JSDocs --- src/blocks/scratch3_pen.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/blocks/scratch3_pen.js b/src/blocks/scratch3_pen.js index 7eff46a0..ac2af450 100644 --- a/src/blocks/scratch3_pen.js +++ b/src/blocks/scratch3_pen.js @@ -183,6 +183,12 @@ class Scratch3PenBlocks { } } + /** + * Wrap a color input into the range (0,100). + * @param {number} value - the value to be wrapped. + * @returns {number} the wrapped value. + * @private + */ _wrapColor (value) { return MathUtil.wrapClamp(value, 0, 100); } @@ -417,6 +423,14 @@ class Scratch3PenBlocks { penState.penAttributes.color4f[3] = this._transparencyToAlpha(penState.transparency); } + /** + * Set or change a single color parameter on the pen state, and update the pen color. + * @param {ColorParam} param - the name of the color parameter to set or change. + * @param {number} value - the value to set or change the param by. + * @param {PenState} penState - the pen state to update. + * @param {boolean} change - if true change param by value, if false set param to value. + * @private + */ _setOrChangeColorParam (param, value, penState, change) { switch (param) { case ColorParam.COLOR: @@ -438,11 +452,27 @@ class Scratch3PenBlocks { this._updatePenColor(penState); } + /** + * The "change pen {ColorParam} by {number}" block changes one of the pen's color parameters + * by a given amound. + * @param {object} args - the block arguments. + * @property {ColorParam} COLOR_PARAM - the name of the selected color parameter. + * @property {number} VALUE - the amount to change the selected parameter by. + * @param {object} util - utility object provided by the runtime. + */ changePenColorParamBy (args, util) { const penState = this._getPenState(util.target); this._setOrChangeColorParam(args.COLOR_PARAM, Cast.toNumber(args.VALUE), penState, true); } + /** + * The "set pen {ColorParam} to {number}" block sets one of the pen's color parameters + * to a given amound. + * @param {object} args - the block arguments. + * @property {ColorParam} COLOR_PARAM - the name of the selected color parameter. + * @property {number} VALUE - the amount to set the selected parameter to. + * @param {object} util - utility object provided by the runtime. + */ setPenColorParamTo (args, util) { const penState = this._getPenState(util.target); this._setOrChangeColorParam(args.COLOR_PARAM, Cast.toNumber(args.VALUE), penState, false); From 0a18ce728790edf0bdcc7fa1019bf4d0af7ebd7e Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 18 Oct 2017 13:16:24 -0400 Subject: [PATCH 514/721] Lint --- src/blocks/scratch3_pen.js | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/blocks/scratch3_pen.js b/src/blocks/scratch3_pen.js index ac2af450..9ac233c7 100644 --- a/src/blocks/scratch3_pen.js +++ b/src/blocks/scratch3_pen.js @@ -321,7 +321,7 @@ class Scratch3PenBlocks { menus: { colorParam: [ColorParam.COLOR, ColorParam.SATURATION, - ColorParam.BRIGHTNESS, ColorParam.TRANSPARENCY] + ColorParam.BRIGHTNESS, ColorParam.TRANSPARENCY] } }; } @@ -412,7 +412,7 @@ class Scratch3PenBlocks { * @private */ _updatePenColor (penState) { - let rgb = Color.hsvToRgb({ + const rgb = Color.hsvToRgb({ h: penState.color * 360 / 100, s: penState.saturation / 100, v: penState.brightness / 100 @@ -433,21 +433,20 @@ class Scratch3PenBlocks { */ _setOrChangeColorParam (param, value, penState, change) { switch (param) { - case ColorParam.COLOR: - penState.color = this._wrapColor(value + (change ? penState.color : 0)); - break; - case ColorParam.SATURATION: - penState.saturation = this._clampColorParam(value + (change ? penState.saturation : 0)); - break; - case ColorParam.BRIGHTNESS: - penState.brightness = this._clampColorParam(value + (change ? penState.brightness : 0)); - break; - case ColorParam.TRANSPARENCY: - penState.transparency = this._clampColorParam(value + (change ? penState.transparency : 0)); - break; - default: - log.warn(`Tried to set or change unknown color parameter: ${param}`); - + case ColorParam.COLOR: + penState.color = this._wrapColor(value + (change ? penState.color : 0)); + break; + case ColorParam.SATURATION: + penState.saturation = this._clampColorParam(value + (change ? penState.saturation : 0)); + break; + case ColorParam.BRIGHTNESS: + penState.brightness = this._clampColorParam(value + (change ? penState.brightness : 0)); + break; + case ColorParam.TRANSPARENCY: + penState.transparency = this._clampColorParam(value + (change ? penState.transparency : 0)); + break; + default: + log.warn(`Tried to set or change unknown color parameter: ${param}`); } this._updatePenColor(penState); } From b74c82a08a03d94b1cd5768a73329a0eb49eeff0 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Sun, 22 Oct 2017 13:19:34 +0000 Subject: [PATCH 515/721] chore(package): update socket.io-client to version 2.0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dabd01f7..fe720827 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "scratch-render": "latest", "scratch-storage": "^0.2.0", "script-loader": "0.7.0", - "socket.io-client": "1.7.3", + "socket.io-client": "2.0.4", "stats.js": "^0.17.0", "tap": "^10.2.0", "tiny-worker": "^2.1.1", From 44ca62fefca1eba28d52e97939e91beed42c8c68 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 23 Oct 2017 11:17:39 -0400 Subject: [PATCH 516/721] Fix event listeners not being cleaned up Eventually if you hit the green flag a million times with a "green flag => say" stack, you'd get an event emitter memory leak error. --- src/blocks/scratch3_looks.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index 8b9e49cc..632917cb 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -92,6 +92,7 @@ class Scratch3LooksBlocks { this.runtime.renderer.destroySkin(bubbleState.skinId); bubbleState.skinId = null; } + target.removeListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved); } /** From 87a0224790863bb40966c2430cb0cf4426fc7f44 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 23 Oct 2017 11:19:39 -0400 Subject: [PATCH 517/721] Stop hide timeouts from sticking around through green flag/stop. --- src/blocks/scratch3_looks.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index 632917cb..7bea9e76 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -93,6 +93,7 @@ class Scratch3LooksBlocks { bubbleState.skinId = null; } target.removeListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved); + clearTimeout(this._bubbleTimeout); } /** @@ -254,7 +255,8 @@ class Scratch3LooksBlocks { sayforsecs (args, util) { this.say(args, util); return new Promise(resolve => { - setTimeout(() => { + this._bubbleTimeout = setTimeout(() => { + this._bubbleTimeout = null; // Clear say bubble and proceed. this._updateBubble(util.target, 'say', ''); resolve(); @@ -269,7 +271,8 @@ class Scratch3LooksBlocks { thinkforsecs (args, util) { this.think(args, util); return new Promise(resolve => { - setTimeout(() => { + this._bubbleTimeout = setTimeout(() => { + this._bubbleTimeout = null; // Clear say bubble and proceed. this._updateBubble(util.target, 'think', ''); resolve(); From 5ca58c7ceb653e04eed4de282853c30a93d32454 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 23 Oct 2017 11:20:46 -0400 Subject: [PATCH 518/721] Fix new bubbles not moving with their owning sprite. This was the original issue #728 that spurred these changes. --- src/blocks/scratch3_looks.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index 7bea9e76..6a339de3 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -174,6 +174,7 @@ class Scratch3LooksBlocks { bubbleState.drawableId = this.runtime.renderer.createDrawable(); bubbleState.skinId = this.runtime.renderer.createTextSkin(type, text, bubbleState.onSpriteRight, [0, 0]); + bubbleState.visible = true; this.runtime.renderer.setDrawableOrder(bubbleState.drawableId, Infinity); this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, { From 24d550d860c159abd6e7789eea90d1b6e5c2e863 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 23 Oct 2017 13:03:10 -0400 Subject: [PATCH 519/721] Remove "visible" state flag and use target visibility directly. This fixes several other bugs like "saying" from a hidden sprite. --- src/blocks/scratch3_looks.js | 46 ++++++++---------------------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index 6a339de3..eb27d090 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -8,7 +8,6 @@ const RenderedTarget = require('../sprites/rendered-target'); * @property {?int} drawableId - the ID of the associated bubble Drawable, null if none. * @property {string} text - the text of the bubble. * @property {string} type - the type of the bubble, "say" or "think" - * @property {Boolean} visible - whether the bubble is hidden along with its sprite. */ class Scratch3LooksBlocks { @@ -38,8 +37,7 @@ class Scratch3LooksBlocks { onSpriteRight: true, skinId: null, text: '', - type: 'say', - visible: true + type: 'say' }; } @@ -72,7 +70,7 @@ class Scratch3LooksBlocks { */ _onTargetMoved (target) { const bubbleState = this._getBubbleState(target); - if (bubbleState.drawableId && bubbleState.visible) { + if (bubbleState.drawableId) { this._positionBubble(target); } } @@ -84,16 +82,14 @@ class Scratch3LooksBlocks { */ _onTargetWillExit (target) { const bubbleState = this._getBubbleState(target); - if (bubbleState.drawableId) { + if (bubbleState.drawableId && bubbleState.skinId) { this.runtime.renderer.destroyDrawable(bubbleState.drawableId); - bubbleState.drawableId = null; - } - if (bubbleState.skinId) { this.runtime.renderer.destroySkin(bubbleState.skinId); + bubbleState.drawableId = null; bubbleState.skinId = null; + this.runtime.requestRedraw(); } target.removeListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved); - clearTimeout(this._bubbleTimeout); } /** @@ -104,6 +100,7 @@ class Scratch3LooksBlocks { for (let n = 0; n < this.runtime.targets.length; n++) { this._onTargetWillExit(this.runtime.targets[n]); } + clearTimeout(this._bubbleTimeout); } /** @@ -150,17 +147,12 @@ class Scratch3LooksBlocks { _renderBubble (target) { const bubbleState = this._getBubbleState(target); const {type, text, onSpriteRight} = bubbleState; - if (text === '') { - return this._setBubbleVisibility(target, false); + // Remove the bubble if empty text or sprite is not visible + if (text === '' || !target.visible) { + return this._onTargetWillExit(target); } if (bubbleState.skinId) { - if (!bubbleState.visible) { - bubbleState.visible = true; - this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, { - visible: bubbleState.visible - }); - } this.runtime.renderer.updateTextSkin(bubbleState.skinId, type, text, onSpriteRight, [0, 0]); } else { target.addListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved); @@ -174,7 +166,6 @@ class Scratch3LooksBlocks { bubbleState.drawableId = this.runtime.renderer.createDrawable(); bubbleState.skinId = this.runtime.renderer.createTextSkin(type, text, bubbleState.onSpriteRight, [0, 0]); - bubbleState.visible = true; this.runtime.renderer.setDrawableOrder(bubbleState.drawableId, Infinity); this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, { @@ -200,23 +191,6 @@ class Scratch3LooksBlocks { this._renderBubble(target); } - /** - * Hide the bubble for a given target. - * @param {!Target} target Target that say/think blocks are being called on. - * @param {!boolean} visibility Visible or not. - * @private - */ - _setBubbleVisibility (target, visibility) { - const bubbleState = this._getBubbleState(target); - bubbleState.visible = visibility; - if (bubbleState.drawableId) { - this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, { - visible: bubbleState.visible - }); - } - this.runtime.requestRedraw(); - } - /** * Retrieve the block primitives implemented by this package. * @return {object.} Mapping of opcode to Function. @@ -288,7 +262,7 @@ class Scratch3LooksBlocks { hide (args, util) { util.target.setVisible(false); - this._setBubbleVisibility(util.target, false); + this._renderBubble(util.target); } /** From 09bb01893c3006d96a2ce15a5569ebc827f9c373 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 23 Oct 2017 14:24:37 -0400 Subject: [PATCH 520/721] Add back the visibility optimization in a simpler way. --- src/blocks/scratch3_looks.js | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index eb27d090..600f003d 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -6,6 +6,8 @@ const RenderedTarget = require('../sprites/rendered-target'); * @typedef {object} BubbleState - the bubble state associated with a particular target. * @property {Boolean} onSpriteRight - tracks whether the bubble is right or left of the sprite. * @property {?int} drawableId - the ID of the associated bubble Drawable, null if none. + * @property {Boolean} drawableVisible - if drawable has been hidden by blank text. + * See _renderBubble for explanation of this optimization. * @property {string} text - the text of the bubble. * @property {string} type - the type of the bubble, "say" or "think" */ @@ -34,6 +36,7 @@ class Scratch3LooksBlocks { static get DEFAULT_BUBBLE_STATE () { return { drawableId: null, + drawableVisible: true, onSpriteRight: true, skinId: null, text: '', @@ -87,6 +90,7 @@ class Scratch3LooksBlocks { this.runtime.renderer.destroySkin(bubbleState.skinId); bubbleState.drawableId = null; bubbleState.skinId = null; + bubbleState.drawableVisible = true; // Reset back to default value this.runtime.requestRedraw(); } target.removeListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved); @@ -146,14 +150,26 @@ class Scratch3LooksBlocks { */ _renderBubble (target) { const bubbleState = this._getBubbleState(target); - const {type, text, onSpriteRight} = bubbleState; - // Remove the bubble if empty text or sprite is not visible - if (text === '' || !target.visible) { + const {drawableVisible, type, text, onSpriteRight} = bubbleState; + + // Remove the bubble if target is not visible, or text is being set to blank + // without being initialized. See comment below about blank text optimization. + if (!target.visible || (text === '' && !bubbleState.skinId)) { return this._onTargetWillExit(target); } if (bubbleState.skinId) { - this.runtime.renderer.updateTextSkin(bubbleState.skinId, type, text, onSpriteRight, [0, 0]); + // Optimization: if text is set to blank, hide the drawable instead of + // getting rid of it. This prevents flickering in "typewriter" projects + if ((text === '' && drawableVisible) || (text !== '' && !drawableVisible)) { + bubbleState.drawableVisible = text !== ''; + this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, { + visible: bubbleState.drawableVisible + }); + } + if (bubbleState.drawableVisible) { + this.runtime.renderer.updateTextSkin(bubbleState.skinId, type, text, onSpriteRight, [0, 0]); + } } else { target.addListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved); From 5e4bdb2bc3dfc805075fca548e7857261552c90e Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Tue, 24 Oct 2017 02:08:45 +0000 Subject: [PATCH 521/721] chore(package): update worker-loader to version 1.1.0 Closes #680 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dabd01f7..f02d68f2 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,6 @@ "tiny-worker": "^2.1.1", "webpack": "^2.4.1", "webpack-dev-server": "^2.4.1", - "worker-loader": "0.8.1" + "worker-loader": "1.1.0" } } From c14155c26de8e305909b650fb3d84cdf93635ce8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 24 Oct 2017 08:17:31 -0400 Subject: [PATCH 522/721] Fix comment on drawableVisible property --- src/blocks/scratch3_looks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index 600f003d..70ffd777 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -6,7 +6,7 @@ const RenderedTarget = require('../sprites/rendered-target'); * @typedef {object} BubbleState - the bubble state associated with a particular target. * @property {Boolean} onSpriteRight - tracks whether the bubble is right or left of the sprite. * @property {?int} drawableId - the ID of the associated bubble Drawable, null if none. - * @property {Boolean} drawableVisible - if drawable has been hidden by blank text. + * @property {Boolean} drawableVisible - false if drawable has been hidden by blank text. * See _renderBubble for explanation of this optimization. * @property {string} text - the text of the bubble. * @property {string} type - the type of the bubble, "say" or "think" From ec1d8c657eb1753756c5cf8e23999badd5e309d8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 25 Oct 2017 10:14:03 -0400 Subject: [PATCH 523/721] Add failing test for callNoReturn on undefined proc --- test/unit/blocks_procedures.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 test/unit/blocks_procedures.js diff --git a/test/unit/blocks_procedures.js b/test/unit/blocks_procedures.js new file mode 100644 index 00000000..4ecdb41d --- /dev/null +++ b/test/unit/blocks_procedures.js @@ -0,0 +1,28 @@ +const test = require('tap').test; +const Procedures = require('../../src/blocks/scratch3_procedures'); + +const blocks = new Procedures(null); + +test('getPrimitives', t => { + t.type(blocks.getPrimitives(), 'object'); + t.end(); +}); + +// Originally inspired by https://github.com/LLK/scratch-gui/issues/809 +test('calling a custom block with no definition does not throw', t => { + const args = { + mutation: { + proccode: 'undefined proc' + } + }; + const util = { + getProcedureParamNames: () => null, + stackFrame: { + executed: false + } + }; + t.doesNotThrow(() => { + blocks.callNoReturn(args, util); + }); + t.end(); +}); From bbbd3dd64b8a74933a24ed3e85cfbb428a8e1c98 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 25 Oct 2017 10:14:54 -0400 Subject: [PATCH 524/721] Make test pass for undefined proc execution --- src/blocks/scratch3_procedures.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/blocks/scratch3_procedures.js b/src/blocks/scratch3_procedures.js index c648d960..b91630f1 100644 --- a/src/blocks/scratch3_procedures.js +++ b/src/blocks/scratch3_procedures.js @@ -27,11 +27,20 @@ class Scratch3ProcedureBlocks { if (!util.stackFrame.executed) { const procedureCode = args.mutation.proccode; const paramNames = util.getProcedureParamNames(procedureCode); + + // If null, procedure could not be found, which can happen if custom + // block is dragged between sprites without the definition. + // Match Scratch 2.0 behavior and noop. + if (paramNames === null) { + return; + } + for (let i = 0; i < paramNames.length; i++) { if (args.hasOwnProperty(`input${i}`)) { util.pushParam(paramNames[i], args[`input${i}`]); } } + util.stackFrame.executed = true; util.startProcedure(procedureCode); } From d126f799d817973ef597785727b8bd2ccdcf0f31 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 25 Oct 2017 11:16:42 -0400 Subject: [PATCH 525/721] Prevent imported local variables from being duplicated on the stage. --- src/engine/blocks.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index f25827b3..ba9077fc 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -255,7 +255,13 @@ class Blocks { }); break; case 'var_create': - stage.createVariable(e.varId, e.varName); + // New variables being created by the user are all global. + // Check if this variable exists on the current target or stage. + // If not, create it on the stage. + // TODO create global and local variables when UI provides a way. + if (!optRuntime.getEditingTarget().lookupVariableById(e.varId)) { + stage.createVariable(e.varId, e.varName); + } break; case 'var_rename': stage.renameVariable(e.varId, e.newName); From 48d3b20d6959fad94cd68f5f0a1cfcc096bef9d9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 30 Oct 2017 15:15:38 -0400 Subject: [PATCH 526/721] Stringify things before saying --- src/blocks/scratch3_looks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index 70ffd777..df1a0246 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -240,7 +240,7 @@ class Scratch3LooksBlocks { say (args, util) { // @TODO in 2.0 calling say/think resets the right/left bias of the bubble - this._updateBubble(util.target, 'say', args.MESSAGE); + this._updateBubble(util.target, 'say', String(args.MESSAGE)); } sayforsecs (args, util) { @@ -256,7 +256,7 @@ class Scratch3LooksBlocks { } think (args, util) { - this._updateBubble(util.target, 'think', args.MESSAGE); + this._updateBubble(util.target, 'think', String(args.MESSAGE)); } thinkforsecs (args, util) { From e4381b4693dbed9746992c9a5b3be73b078a2171 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Tue, 31 Oct 2017 11:28:56 -0700 Subject: [PATCH 527/721] Add an init watchdog for extension workers If an extension worker does not complete initialization in a reasonable amount of time (currently 15 seconds) then the worker will be terminated. A complete initialization includes registering an extension. --- src/dispatch/central-dispatch.js | 21 +++++- src/dispatch/shared-dispatch.js | 31 +++++++-- src/extension-support/extension-manager.js | 75 ++++++++++++++++++++-- 3 files changed, 117 insertions(+), 10 deletions(-) diff --git a/src/dispatch/central-dispatch.js b/src/dispatch/central-dispatch.js index 6dd23625..a60f9d97 100644 --- a/src/dispatch/central-dispatch.js +++ b/src/dispatch/central-dispatch.js @@ -38,7 +38,7 @@ class CentralDispatch extends SharedDispatch { /** * Set a local object as the global provider of the specified service. * WARNING: Any method on the provider can be called from any worker within the dispatch system. - * @param {string} service - a globally unique string identifying this service. Examples: 'vm', 'gui', 'extension9'. + * @param {string} service - a globally unique string identifying this service. Examples: 'vm', 'gui'. * @param {object} provider - a local object which provides this service. * @returns {Promise} - a promise which will resolve once the service is registered. */ @@ -72,6 +72,25 @@ class CentralDispatch extends SharedDispatch { } } + /** + * Remove a service provider from the central dispatch service, un-registering any services it provides. + * If the provider is a worker its dispatch service will be shut down. + * The worker itself will NOT be terminated by this call. + * @param {Worker|object} provider - the worker or object instance to be removed. + */ + removeProvider (provider) { + const workerIndex = this.workers.indexOf(provider); + if (workerIndex !== -1) { + /** @TODO Should we ask the worker to shut down? If so, owner must wait before terminating the worker. */ + this.workers.splice(workerIndex, 1); + } + for (const serviceName in Object.keys(this.services)) { + if (this.services[serviceName] === provider) { + delete this.services[serviceName]; + } + } + } + /** * Fetch the service provider object for a particular service name. * @override diff --git a/src/dispatch/shared-dispatch.js b/src/dispatch/shared-dispatch.js index 1e4785bc..7c907d52 100644 --- a/src/dispatch/shared-dispatch.js +++ b/src/dispatch/shared-dispatch.js @@ -39,6 +39,24 @@ class SharedDispatch { * @type {int} */ this.nextResponseId = 0; + + /** + * While processing a call in _onMessage, this will be set to the originating object. + * At all other times it will be null. + * @type {object} + * @private + */ + this._callingWorker = null; + } + + /** + * Central dispatch: while processing a call from a worker, this will be set to the worker originating that call. + * Worker dispatch: while processing a call from the main thread, this will be set to the global object. + * At all other times it will be null. + * @returns {object} - the worker originating the current call, if any. + */ + get callingWorker () { + return this._callingWorker; } /** @@ -184,10 +202,15 @@ class SharedDispatch { message.args = message.args || []; let promise; if (message.service) { - if (message.service === 'dispatch') { - promise = this._onDispatchMessage(worker, message); - } else { - promise = this.call(message.service, message.method, ...message.args); + try { + this._callingWorker = worker; + if (message.service === 'dispatch') { + promise = this._onDispatchMessage(worker, message); + } else { + promise = this.call(message.service, message.method, ...message.args); + } + } finally { + this._callingWorker = null; } } else if (typeof message.responseId === 'undefined') { log.error(`Dispatch caught malformed message from a worker: ${JSON.stringify(event)}`); diff --git a/src/extension-support/extension-manager.js b/src/extension-support/extension-manager.js index d7c5fab0..31e915f8 100644 --- a/src/extension-support/extension-manager.js +++ b/src/extension-support/extension-manager.js @@ -70,6 +70,14 @@ class ExtensionManager { */ this.pendingWorkers = []; + /** + * Set of workers currently being monitored by `_startWorkerWatchdog`. + * @see {_startWorkerWatchdog} + * @type {Set.} + * @private + */ + this._activeWatchdogs = new Set(); + /** * Keep a reference to the runtime so we can construct internal extension objects. * TODO: remove this in favor of extensions accessing the runtime as a service. @@ -99,7 +107,10 @@ class ExtensionManager { const ExtensionWorker = require('worker-loader?name=extension-worker.js!./extension-worker'); this.pendingExtensions.push({extensionURL, resolve, reject}); - dispatch.addWorker(new ExtensionWorker()); + + const worker = new ExtensionWorker(); + dispatch.addWorker(worker); + this._startWorkerWatchdog(worker); }); } @@ -110,16 +121,28 @@ class ExtensionManager { return [id, workerInfo.extensionURL]; } + /** + * Collect extension metadata from the specified service and begin the extension registration process. + * @param {string} serviceName - the name of the service hosting the extension. + */ registerExtensionService (serviceName) { dispatch.call(serviceName, 'getInfo').then(info => { this._registerExtensionInfo(serviceName, info); }); } + /** + * Called by an extension worker to indicate that the worker has finished initialization. + * @param {int} id - the worker ID. + * @param {*?} e - the error encountered during initialization, if any. + */ onWorkerInit (id, e) { const workerInfo = this.pendingWorkers[id]; delete this.pendingWorkers[id]; if (e) { + log.warn(`Extension manager forcibly terminating worker for extension with ID ${id}, URL ${ + workerInfo.extensionURL} due to error during initialization`); + workerInfo.worker.terminate(); workerInfo.reject(e); } else { workerInfo.resolve(id); @@ -133,16 +156,29 @@ class ExtensionManager { */ _registerInternalExtension (extensionObject) { const extensionInfo = extensionObject.getInfo(); - const serviceName = `extension.internal.${extensionInfo.id}`; + const fakeWorkerId = this.nextExtensionWorker++; + const serviceName = `extension.${fakeWorkerId}.${extensionInfo.id}`; return dispatch.setService(serviceName, extensionObject) .then(() => dispatch.call('extensions', 'registerExtensionService', serviceName)); } + /** + * Sanitize extension info then register its primitives with the VM. + * @param {string} serviceName - the name of the service hosting the extension + * @param {ExtensionInfo} extensionInfo - the extension's metadata + * @private + */ _registerExtensionInfo (serviceName, extensionInfo) { extensionInfo = this._prepareExtensionInfo(serviceName, extensionInfo); - dispatch.call('runtime', '_registerExtensionPrimitives', extensionInfo).catch(e => { - log.error(`Failed to register primitives for extension on service ${serviceName}: ${JSON.stringify(e)}`); - }); + dispatch.call('runtime', '_registerExtensionPrimitives', extensionInfo).then( + () => { + if (dispatch.callingWorker) { + this._stopWorkerWatchdog(dispatch.callingWorker); + } + }, + e => { + log.error(`Failed to register primitives for extension "${extensionInfo.id}": ${e.message}`); + }); } /** @@ -211,6 +247,35 @@ class ExtensionManager { } return blockInfo; } + + /** + * Start a watchdog to terminate this worker unless it registers an extension before the timeout. + * @param {object} worker - the worker to watch. + * @private + */ + _startWorkerWatchdog (worker) { + const timeout = 5 * 1000; // 5 seconds + + this._activeWatchdogs.add(worker); + setTimeout(() => { + if (this._activeWatchdogs.has(worker)) { + this._activeWatchdogs.delete(worker); + log.warn(`Worker watchdog timed out. Terminating worker.`); + dispatch.removeProvider(worker); + worker.terminate(); + } + }, timeout); + } + + /** + * Mark this worker as "safe" from the watchdog. + * @param {object} worker - the worker to mark as safe. + * @see {_startWorkerWatchdog} + * @private + */ + _stopWorkerWatchdog (worker) { + this._activeWatchdogs.delete(worker); + } } module.exports = ExtensionManager; From 670e51d33580e8a2e852b3b038bb3afc282f81b9 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Tue, 31 Oct 2017 11:32:21 -0700 Subject: [PATCH 528/721] Refuse to register second extension with same ID If an extension attempts to register with the same ID as another extension which has already registered, the new registration is refused. If the extension is in a worker and no other extension is successfully registered by that worker, the watchdog system will terminate the "empty" worker. --- src/dispatch/central-dispatch.js | 13 +++++++ src/extension-support/extension-manager.js | 43 +++++++++++++++++----- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/dispatch/central-dispatch.js b/src/dispatch/central-dispatch.js index a60f9d97..9b968172 100644 --- a/src/dispatch/central-dispatch.js +++ b/src/dispatch/central-dispatch.js @@ -55,6 +55,19 @@ class CentralDispatch extends SharedDispatch { } } + /** + * Unregister a service without regard to which object or worker might provide it. + * @param {string} service - the globally unique string provided to `setService` when the service was registered. + * @see {setService} + */ + clearService (service) { + if (this.services.hasOwnProperty(service)) { + delete this.services[service]; + } else { + log.warn(`Central dispatch can't clear unknown service ${service}`); + } + } + /** * Add a worker to the message dispatch system. The worker must implement a compatible message dispatch framework. * The dispatcher will immediately attempt to "handshake" with the worker. diff --git a/src/extension-support/extension-manager.js b/src/extension-support/extension-manager.js index 31e915f8..f671fa53 100644 --- a/src/extension-support/extension-manager.js +++ b/src/extension-support/extension-manager.js @@ -70,6 +70,13 @@ class ExtensionManager { */ this.pendingWorkers = []; + /** + * Set of loaded extension IDs. For built-in extensions the "URL" is the same as the ID; they differ in general. + * @type {Set.} + * @private + */ + this._loadedExtensions = new Set(); + /** * Set of workers currently being monitored by `_startWorkerWatchdog`. * @see {_startWorkerWatchdog} @@ -90,6 +97,17 @@ class ExtensionManager { }); } + /** + * Check whether an extension is registered or is in the process of loading. This is intended to control loading or + * adding extensions so it may return `true` before the extension is ready to be used. Use the promise returned by + * `loadExtensionURL` if you need to wait until the extension is truly ready. + * @param {string} extensionID - the ID (not URL) of the extension. + * @returns {boolean} - true if loaded, false otherwise. + */ + isExtensionLoaded (extensionID) { + return this._loadedExtensions.has(extensionID); + } + /** * Load an extension by URL or internal extension ID * @param {string} extensionURL - the URL for the extension to load OR the ID of an internal extension @@ -170,15 +188,22 @@ class ExtensionManager { */ _registerExtensionInfo (serviceName, extensionInfo) { extensionInfo = this._prepareExtensionInfo(serviceName, extensionInfo); - dispatch.call('runtime', '_registerExtensionPrimitives', extensionInfo).then( - () => { - if (dispatch.callingWorker) { - this._stopWorkerWatchdog(dispatch.callingWorker); - } - }, - e => { - log.error(`Failed to register primitives for extension "${extensionInfo.id}": ${e.message}`); - }); + if (this.isExtensionLoaded(extensionInfo.id)) { + const message = `Ignoring attempt to load a second extension with ID ${extensionInfo.id}`; + log.warn(message); + dispatch.clearService(serviceName); + } else { + dispatch.call('runtime', '_registerExtensionPrimitives', extensionInfo).then( + () => { + this._loadedExtensions.add(extensionInfo.id); + if (dispatch.callingWorker) { + this._stopWorkerWatchdog(dispatch.callingWorker); + } + }, + e => { + log.error(`Failed to register primitives for extension "${extensionInfo.id}": ${e.message}`); + }); + } } /** From ae878b2050fb3d7f4fe28604856fea626791644e Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Tue, 31 Oct 2017 12:35:18 -0700 Subject: [PATCH 529/721] Simplify extension duplication prevention station Since we will only have builtin extensions for a while, the more-complicated, watchdog-based extension duplication prevention has been replaced with one that treats the extension URL as equivalent to the extension ID. This is true only for built-in extensions, so once we start supporting third-party extensions we will likely need to bring back the watchdog (commit e4381b4693dbed9746992c9a5b3be73b078a2171) and the more-complex duplication prevention (commit 670e51d33580e8a2e852b3b038bb3afc282f81b9). The code here should be treated as a last resort: ideally the GUI should check if a particular extension is loaded (maybe by calling the Extension Manager's new `isExtensionLoaded` method) and avoid calling `loadExtensionURL` if it is. --- src/dispatch/central-dispatch.js | 34 +--------- src/dispatch/shared-dispatch.js | 31 ++------- src/extension-support/extension-manager.js | 75 ++++------------------ 3 files changed, 18 insertions(+), 122 deletions(-) diff --git a/src/dispatch/central-dispatch.js b/src/dispatch/central-dispatch.js index 9b968172..6dd23625 100644 --- a/src/dispatch/central-dispatch.js +++ b/src/dispatch/central-dispatch.js @@ -38,7 +38,7 @@ class CentralDispatch extends SharedDispatch { /** * Set a local object as the global provider of the specified service. * WARNING: Any method on the provider can be called from any worker within the dispatch system. - * @param {string} service - a globally unique string identifying this service. Examples: 'vm', 'gui'. + * @param {string} service - a globally unique string identifying this service. Examples: 'vm', 'gui', 'extension9'. * @param {object} provider - a local object which provides this service. * @returns {Promise} - a promise which will resolve once the service is registered. */ @@ -55,19 +55,6 @@ class CentralDispatch extends SharedDispatch { } } - /** - * Unregister a service without regard to which object or worker might provide it. - * @param {string} service - the globally unique string provided to `setService` when the service was registered. - * @see {setService} - */ - clearService (service) { - if (this.services.hasOwnProperty(service)) { - delete this.services[service]; - } else { - log.warn(`Central dispatch can't clear unknown service ${service}`); - } - } - /** * Add a worker to the message dispatch system. The worker must implement a compatible message dispatch framework. * The dispatcher will immediately attempt to "handshake" with the worker. @@ -85,25 +72,6 @@ class CentralDispatch extends SharedDispatch { } } - /** - * Remove a service provider from the central dispatch service, un-registering any services it provides. - * If the provider is a worker its dispatch service will be shut down. - * The worker itself will NOT be terminated by this call. - * @param {Worker|object} provider - the worker or object instance to be removed. - */ - removeProvider (provider) { - const workerIndex = this.workers.indexOf(provider); - if (workerIndex !== -1) { - /** @TODO Should we ask the worker to shut down? If so, owner must wait before terminating the worker. */ - this.workers.splice(workerIndex, 1); - } - for (const serviceName in Object.keys(this.services)) { - if (this.services[serviceName] === provider) { - delete this.services[serviceName]; - } - } - } - /** * Fetch the service provider object for a particular service name. * @override diff --git a/src/dispatch/shared-dispatch.js b/src/dispatch/shared-dispatch.js index 7c907d52..1e4785bc 100644 --- a/src/dispatch/shared-dispatch.js +++ b/src/dispatch/shared-dispatch.js @@ -39,24 +39,6 @@ class SharedDispatch { * @type {int} */ this.nextResponseId = 0; - - /** - * While processing a call in _onMessage, this will be set to the originating object. - * At all other times it will be null. - * @type {object} - * @private - */ - this._callingWorker = null; - } - - /** - * Central dispatch: while processing a call from a worker, this will be set to the worker originating that call. - * Worker dispatch: while processing a call from the main thread, this will be set to the global object. - * At all other times it will be null. - * @returns {object} - the worker originating the current call, if any. - */ - get callingWorker () { - return this._callingWorker; } /** @@ -202,15 +184,10 @@ class SharedDispatch { message.args = message.args || []; let promise; if (message.service) { - try { - this._callingWorker = worker; - if (message.service === 'dispatch') { - promise = this._onDispatchMessage(worker, message); - } else { - promise = this.call(message.service, message.method, ...message.args); - } - } finally { - this._callingWorker = null; + if (message.service === 'dispatch') { + promise = this._onDispatchMessage(worker, message); + } else { + promise = this.call(message.service, message.method, ...message.args); } } else if (typeof message.responseId === 'undefined') { log.error(`Dispatch caught malformed message from a worker: ${JSON.stringify(event)}`); diff --git a/src/extension-support/extension-manager.js b/src/extension-support/extension-manager.js index f671fa53..d9dc3603 100644 --- a/src/extension-support/extension-manager.js +++ b/src/extension-support/extension-manager.js @@ -71,20 +71,12 @@ class ExtensionManager { this.pendingWorkers = []; /** - * Set of loaded extension IDs. For built-in extensions the "URL" is the same as the ID; they differ in general. + * Set of loaded extension URLs/IDs (equivalent for built-in extensions). * @type {Set.} * @private */ this._loadedExtensions = new Set(); - /** - * Set of workers currently being monitored by `_startWorkerWatchdog`. - * @see {_startWorkerWatchdog} - * @type {Set.} - * @private - */ - this._activeWatchdogs = new Set(); - /** * Keep a reference to the runtime so we can construct internal extension objects. * TODO: remove this in favor of extensions accessing the runtime as a service. @@ -101,7 +93,7 @@ class ExtensionManager { * Check whether an extension is registered or is in the process of loading. This is intended to control loading or * adding extensions so it may return `true` before the extension is ready to be used. Use the promise returned by * `loadExtensionURL` if you need to wait until the extension is truly ready. - * @param {string} extensionID - the ID (not URL) of the extension. + * @param {string} extensionID - the ID of the extension. * @returns {boolean} - true if loaded, false otherwise. */ isExtensionLoaded (extensionID) { @@ -115,6 +107,13 @@ class ExtensionManager { */ loadExtensionURL (extensionURL) { if (builtinExtensions.hasOwnProperty(extensionURL)) { + /** @TODO dupe handling for non-builtin extensions. See commit 670e51d33580e8a2e852b3b038bb3afc282f81b9 */ + if (this.isExtensionLoaded(extensionURL)) { + const message = `Rejecting attempt to load a second extension with ID ${extensionURL}`; + log.warn(message); + return Promise.reject(new Error(message)); + } + const extension = builtinExtensions[extensionURL]; const extensionInstance = new extension(this.runtime); return this._registerInternalExtension(extensionInstance); @@ -125,10 +124,7 @@ class ExtensionManager { const ExtensionWorker = require('worker-loader?name=extension-worker.js!./extension-worker'); this.pendingExtensions.push({extensionURL, resolve, reject}); - - const worker = new ExtensionWorker(); - dispatch.addWorker(worker); - this._startWorkerWatchdog(worker); + dispatch.addWorker(new ExtensionWorker()); }); } @@ -158,9 +154,6 @@ class ExtensionManager { const workerInfo = this.pendingWorkers[id]; delete this.pendingWorkers[id]; if (e) { - log.warn(`Extension manager forcibly terminating worker for extension with ID ${id}, URL ${ - workerInfo.extensionURL} due to error during initialization`); - workerInfo.worker.terminate(); workerInfo.reject(e); } else { workerInfo.resolve(id); @@ -188,22 +181,9 @@ class ExtensionManager { */ _registerExtensionInfo (serviceName, extensionInfo) { extensionInfo = this._prepareExtensionInfo(serviceName, extensionInfo); - if (this.isExtensionLoaded(extensionInfo.id)) { - const message = `Ignoring attempt to load a second extension with ID ${extensionInfo.id}`; - log.warn(message); - dispatch.clearService(serviceName); - } else { - dispatch.call('runtime', '_registerExtensionPrimitives', extensionInfo).then( - () => { - this._loadedExtensions.add(extensionInfo.id); - if (dispatch.callingWorker) { - this._stopWorkerWatchdog(dispatch.callingWorker); - } - }, - e => { - log.error(`Failed to register primitives for extension "${extensionInfo.id}": ${e.message}`); - }); - } + dispatch.call('runtime', '_registerExtensionPrimitives', extensionInfo).catch(e => { + log.error(`Failed to register primitives for extension on service ${serviceName}: ${JSON.stringify(e)}`); + }); } /** @@ -272,35 +252,6 @@ class ExtensionManager { } return blockInfo; } - - /** - * Start a watchdog to terminate this worker unless it registers an extension before the timeout. - * @param {object} worker - the worker to watch. - * @private - */ - _startWorkerWatchdog (worker) { - const timeout = 5 * 1000; // 5 seconds - - this._activeWatchdogs.add(worker); - setTimeout(() => { - if (this._activeWatchdogs.has(worker)) { - this._activeWatchdogs.delete(worker); - log.warn(`Worker watchdog timed out. Terminating worker.`); - dispatch.removeProvider(worker); - worker.terminate(); - } - }, timeout); - } - - /** - * Mark this worker as "safe" from the watchdog. - * @param {object} worker - the worker to mark as safe. - * @see {_startWorkerWatchdog} - * @private - */ - _stopWorkerWatchdog (worker) { - this._activeWatchdogs.delete(worker); - } } module.exports = ExtensionManager; From 8bb48e2a53c80efd922911ae770dad09ca8de3d8 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Tue, 31 Oct 2017 13:00:19 -0700 Subject: [PATCH 530/721] Add built-in extension "URL" to _loadedExtensions Dupe prevention works better when you actually record the necessary info... --- src/extension-support/extension-manager.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/extension-support/extension-manager.js b/src/extension-support/extension-manager.js index d9dc3603..ef3d56aa 100644 --- a/src/extension-support/extension-manager.js +++ b/src/extension-support/extension-manager.js @@ -116,7 +116,9 @@ class ExtensionManager { const extension = builtinExtensions[extensionURL]; const extensionInstance = new extension(this.runtime); - return this._registerInternalExtension(extensionInstance); + return this._registerInternalExtension(extensionInstance).then(() => { + this._loadedExtensions.add(extensionURL); + }); } return new Promise((resolve, reject) => { From 082e051cce2d84838aa9a5aef4c3b48b83a71845 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 31 Oct 2017 17:24:12 -0400 Subject: [PATCH 531/721] Support legacy pen block op codes --- src/blocks/scratch3_pen.js | 129 +++++++++++++++++++++++++++++++ src/serialization/sb2_specmap.js | 4 +- 2 files changed, 131 insertions(+), 2 deletions(-) diff --git a/src/blocks/scratch3_pen.js b/src/blocks/scratch3_pen.js index 9ac233c7..d50bbe75 100644 --- a/src/blocks/scratch3_pen.js +++ b/src/blocks/scratch3_pen.js @@ -71,6 +71,7 @@ class Scratch3PenBlocks { saturation: 100, brightness: 100, transparency: 0, + _shade: 50, // Used only for legacy `change shade by` blocks penAttributes: { color4f: [0, 0, 1, 1], diameter: 1 @@ -316,6 +317,51 @@ class Scratch3PenBlocks { defaultValue: 1 } } + }, + /* Legacy blocks, should not be shown in flyout */ + { + opcode: 'setPenShadeToNumber', + blockType: BlockType.COMMAND, + text: 'set pen shade to [SHADE]', + arguments: { + SHADE: { + type: ArgumentType.NUMBER, + defaultValue: 1 + } + } + }, + { + opcode: 'changePenShadeBy', + blockType: BlockType.COMMAND, + text: 'change pen shade by [SHADE]', + arguments: { + SHADE: { + type: ArgumentType.NUMBER, + defaultValue: 1 + } + } + }, + { + opcode: 'setPenHueToNumber', + blockType: BlockType.COMMAND, + text: 'set pen hue to [HUE]', + arguments: { + HUE: { + type: ArgumentType.NUMBER, + defaultValue: 1 + } + } + }, + { + opcode: 'changePenHueBy', + blockType: BlockType.COMMAND, + text: 'change pen hue by [HUE]', + arguments: { + HUE: { + type: ArgumentType.NUMBER, + defaultValue: 1 + } + } } ], menus: { @@ -402,6 +448,10 @@ class Scratch3PenBlocks { penState.saturation = hsv.s * 100; penState.brightness = hsv.v * 100; penState.transparency = 0; + + // Set the legacy "shade" value the same way scratch 2 did. + penState._shade = penState.brightness / 2; + this._updatePenColor(penState); } @@ -498,6 +548,85 @@ class Scratch3PenBlocks { const penAttributes = this._getPenState(util.target).penAttributes; penAttributes.diameter = this._clampPenSize(Cast.toNumber(args.SIZE)); } + + /* LEGACY OPCODES */ + /** + * Scratch 2 "hue" param is equivelant to twice the new "color" param. + * @param {object} args - the block arguments. + * @property {number} HUE - the amount to set the hue to. + * @param {object} util - utility object provided by the runtime. + */ + setPenHueToNumber (args, util) { + const penState = this._getPenState(util.target); + const hueValue = Cast.toNumber(args.HUE); + const colorValue = hueValue / 2; + this._setOrChangeColorParam(ColorParam.COLOR, colorValue, penState, false); + } + + /** + * Scratch 2 "hue" param is equivelant to twice the new "color" param. + * @param {object} args - the block arguments. + * @property {number} HUE - the amount of desired hue change. + * @param {object} util - utility object provided by the runtime. + */ + changePenHueBy (args, util) { + const penState = this._getPenState(util.target); + const hueChange = Cast.toNumber(args.HUE); + const colorChange = hueChange / 2; + this._setOrChangeColorParam(ColorParam.COLOR, colorChange, penState, true); + } + + /** + * Use legacy "set shade" code to calculate RGB value for shade, + * then convert back to HSV and store those components. + * It is important to also track the given shade in penState._shade + * because it cannot be accurately backed out of the new HSV later. + * @param {object} args - the block arguments. + * @property {number} SHADE - the amount to set the shade to. + * @param {object} util - utility object provided by the runtime. + */ + setPenShadeToNumber (args, util) { + const penState = this._getPenState(util.target); + let newShade = Cast.toNumber(args.SHADE); + + // Wrap clamp the new shade value the way scratch 2 did. + newShade = newShade % 200; + if (newShade < 0) newShade += 200; + + // Create the new color in RGB using the scratch 2 "shade" model + let rgb = Color.hsvToRgb({h: penState.color * 360 / 100, s: 1, v: 1}); + const shade = (newShade > 100) ? 200 - newShade : newShade; + if (shade < 50) { + rgb = Color.mixRgb(Color.RGB_BLACK, rgb, (10 + shade) / 60); + } else { + rgb = Color.mixRgb(rgb, Color.RGB_WHITE, (shade - 50) / 60); + } + + // Update the pen state according to new color + const hsv = Color.rgbToHsv(rgb); + penState.color = 100 * hsv.h / 360; + penState.saturation = 100 * hsv.s; + penState.brightness = 100 * hsv.v; + + // And store the shade that was used to compute this new color for later use. + penState._shade = newShade; + + this._updatePenColor(penState); + } + + /** + * Because "shade" cannot be backed out of hsv consistently, use the previously + * stored penState._shade to make the shade change. + * @param {object} args - the block arguments. + * @property {number} SHADE - the amount of desired shade change. + * @param {object} util - utility object provided by the runtime. + */ + changePenShadeBy (args, util) { + const penState = this._getPenState(util.target); + const shadeChange = Cast.toNumber(args.SHADE); + this.setPenShadeToNumber({SHADE: penState._shade + shadeChange}, util); + } + } module.exports = Scratch3PenBlocks; diff --git a/src/serialization/sb2_specmap.js b/src/serialization/sb2_specmap.js index 2024b239..1b697934 100644 --- a/src/serialization/sb2_specmap.js +++ b/src/serialization/sb2_specmap.js @@ -535,7 +535,7 @@ const specMap = { { type: 'input', inputOp: 'math_number', - inputName: 'COLOR' + inputName: 'HUE' } ] }, @@ -545,7 +545,7 @@ const specMap = { { type: 'input', inputOp: 'math_number', - inputName: 'COLOR' + inputName: 'HUE' } ] }, From 18398d010b3dfc8e5bc60a8463263c99016db097 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 1 Nov 2017 11:07:26 -0400 Subject: [PATCH 532/721] Add hideFromPalette flag for hiding legacy blocks --- src/blocks/scratch3_pen.js | 12 ++++++++---- src/engine/runtime.js | 3 ++- src/extension-support/extension-manager.js | 1 + 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/blocks/scratch3_pen.js b/src/blocks/scratch3_pen.js index d50bbe75..7de5e51a 100644 --- a/src/blocks/scratch3_pen.js +++ b/src/blocks/scratch3_pen.js @@ -328,7 +328,8 @@ class Scratch3PenBlocks { type: ArgumentType.NUMBER, defaultValue: 1 } - } + }, + hideFromPalette: true }, { opcode: 'changePenShadeBy', @@ -339,7 +340,8 @@ class Scratch3PenBlocks { type: ArgumentType.NUMBER, defaultValue: 1 } - } + }, + hideFromPalette: true }, { opcode: 'setPenHueToNumber', @@ -350,7 +352,8 @@ class Scratch3PenBlocks { type: ArgumentType.NUMBER, defaultValue: 1 } - } + }, + hideFromPalette: true }, { opcode: 'changePenHueBy', @@ -361,7 +364,8 @@ class Scratch3PenBlocks { type: ArgumentType.NUMBER, defaultValue: 1 } - } + }, + hideFromPalette: true } ], menus: { diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 1e0e90ca..b9738713 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -630,8 +630,9 @@ class Runtime extends EventEmitter { const xmlParts = []; for (const categoryInfo of this._blockInfo) { const {name, color1, color2} = categoryInfo; + const paletteBlocks = categoryInfo.blocks.filter(block => !block.info.hideFromPalette); xmlParts.push(``); - xmlParts.push.apply(xmlParts, categoryInfo.blocks.map(blockInfo => blockInfo.xml)); + xmlParts.push.apply(xmlParts, paletteBlocks.map(block => block.xml)); xmlParts.push(''); } return xmlParts.join('\n'); diff --git a/src/extension-support/extension-manager.js b/src/extension-support/extension-manager.js index d7c5fab0..2179e3f2 100644 --- a/src/extension-support/extension-manager.js +++ b/src/extension-support/extension-manager.js @@ -30,6 +30,7 @@ const builtinExtensions = { * @property {object.|undefined} arguments - information about this block's arguments, if any * @property {string|Function|undefined} func - the method for this block on the extension service (default: opcode) * @property {Array.|undefined} filter - the list of targets for which this block should appear (default: all) + * @property {Boolean|undefined} hideFromPalette - true if should not be appear in the palette. (default false) */ /** From 2c3c9f9ac877e10dbcb1979051dd0a4f5cef0ce8 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 1 Nov 2017 14:31:52 -0400 Subject: [PATCH 533/721] Search for variables by name in "of" block --- src/blocks/scratch3_sensing.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index 98eeac97..559b0e9c 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -167,8 +167,10 @@ class Scratch3SensingBlocks { // Variables const varName = args.PROPERTY; - if (attrTarget.variables.hasOwnProperty(varName)) { - return attrTarget.variables[varName].value; + for (const id in attrTarget.variables) { + if (attrTarget.variables[id].name === varName) { + return attrTarget.variables[id].value; + } } // Otherwise, 0 From 54e19b2bb5d8581dff218182b1353b0031cd38e3 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Thu, 2 Nov 2017 20:05:55 +0000 Subject: [PATCH 534/721] chore(package): update scratch-storage to version 0.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a40a0bfe..657022b1 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "scratch-audio": "latest", "scratch-blocks": "latest", "scratch-render": "latest", - "scratch-storage": "^0.2.0", + "scratch-storage": "^0.3.0", "script-loader": "0.7.0", "socket.io-client": "1.7.3", "stats.js": "^0.17.0", From 5b9d4ae23e6da83663d2f34dc986db2d9dd49e70 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 2 Nov 2017 17:03:12 -0400 Subject: [PATCH 535/721] Provide argument id list with callers and procdef blocks --- src/serialization/sb2.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index 5225614a..c56479aa 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -52,6 +52,18 @@ const parseProcedureArgMap = function (procCode) { return argMap; }; +/** + * Generate a list of "argument IDs" for procdefs and caller mutations. + * IDs just end up being `input0`, `input1`, ... which is good enough. + * @param {string} procCode Scratch 2.0 procedure string. + * @return {Array.} Array of argument id strings. + */ +const parseProcedureArgIds = function (procCode) { + return parseProcedureArgMap(procCode) + .map(arg => arg.inputName) + .filter(name => name); // Filter out unnamed inputs which are labels +} + /** * Flatten a block tree into a block list. * Children are temporarily stored on the `block.children` property. @@ -501,6 +513,7 @@ const parseBlock = function (sb2block, getVariableId) { tagName: 'mutation', proccode: procData[0], // e.g., "abc %n %b %s" argumentnames: JSON.stringify(procData[1]), // e.g. ['arg1', 'arg2'] + argumentids: JSON.stringify(parseProcedureArgIds(procData[0])), argumentdefaults: JSON.stringify(procData[2]), // e.g., [1, 'abc'] warp: procData[3], // Warp mode, e.g., true/false. children: [] @@ -512,7 +525,8 @@ const parseBlock = function (sb2block, getVariableId) { activeBlock.mutation = { tagName: 'mutation', children: [], - proccode: sb2block[1] + proccode: sb2block[1], + argumentids: JSON.stringify(parseProcedureArgIds(sb2block[1])) }; } else if (oldOpcode === 'getParam') { // Mutation for procedure parameter. From 007fe2ed53892ba4de9472ca2a8c8f465243846d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 2 Nov 2017 17:14:00 -0400 Subject: [PATCH 536/721] Fix linting --- src/serialization/sb2.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index c56479aa..092bdfc9 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -62,7 +62,7 @@ const parseProcedureArgIds = function (procCode) { return parseProcedureArgMap(procCode) .map(arg => arg.inputName) .filter(name => name); // Filter out unnamed inputs which are labels -} +}; /** * Flatten a block tree into a block list. From a115d47bb88880a593908c6153348994447e7291 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 3 Nov 2017 11:25:39 -0400 Subject: [PATCH 537/721] Add block icon svg as single-line data URI --- src/blocks/scratch3_pen.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/blocks/scratch3_pen.js b/src/blocks/scratch3_pen.js index 7de5e51a..90abf212 100644 --- a/src/blocks/scratch3_pen.js +++ b/src/blocks/scratch3_pen.js @@ -7,6 +7,13 @@ const MathUtil = require('../util/math-util'); const RenderedTarget = require('../sprites/rendered-target'); const log = require('../util/log'); +/** + * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI. + * @type {string} + */ +// eslint-disable-next-line max-len +const iconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+cGVuLWljb248L3RpdGxlPjxnIHN0cm9rZT0iIzU3NUU3NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik04Ljc1MyAzNC42MDJsLTQuMjUgMS43OCAxLjc4My00LjIzN2MxLjIxOC0yLjg5MiAyLjkwNy01LjQyMyA1LjAzLTcuNTM4TDMxLjA2NiA0LjkzYy44NDYtLjg0MiAyLjY1LS40MSA0LjAzMi45NjcgMS4zOCAxLjM3NSAxLjgxNiAzLjE3My45NyA0LjAxNUwxNi4zMTggMjkuNTljLTIuMTIzIDIuMTE2LTQuNjY0IDMuOC03LjU2NSA1LjAxMiIgZmlsbD0iI0ZGRiIvPjxwYXRoIGQ9Ik0yOS40MSA2LjExcy00LjQ1LTIuMzc4LTguMjAyIDUuNzcyYy0xLjczNCAzLjc2Ni00LjM1IDEuNTQ2LTQuMzUgMS41NDYiLz48cGF0aCBkPSJNMzYuNDIgOC44MjVjMCAuNDYzLS4xNC44NzMtLjQzMiAxLjE2NGwtOS4zMzUgOS4zYy4yODItLjI5LjQxLS42NjguNDEtMS4xMiAwLS44NzQtLjUwNy0xLjk2My0xLjQwNi0yLjg2OC0xLjM2Mi0xLjM1OC0zLjE0Ny0xLjgtNC4wMDItLjk5TDMwLjk5IDUuMDFjLjg0NC0uODQgMi42NS0uNDEgNC4wMzUuOTYuODk4LjkwNCAxLjM5NiAxLjk4MiAxLjM5NiAyLjg1NU0xMC41MTUgMzMuNzc0Yy0uNTczLjMwMi0xLjE1Ny41Ny0xLjc2NC44M0w0LjUgMzYuMzgybDEuNzg2LTQuMjM1Yy4yNTgtLjYwNC41My0xLjE4Ni44MzMtMS43NTcuNjkuMTgzIDEuNDQ4LjYyNSAyLjEwOCAxLjI4Mi42Ni42NTggMS4xMDIgMS40MTIgMS4yODcgMi4xMDIiIGZpbGw9IiM0Qzk3RkYiLz48cGF0aCBkPSJNMzYuNDk4IDguNzQ4YzAgLjQ2NC0uMTQuODc0LS40MzMgMS4xNjVsLTE5Ljc0MiAxOS42OGMtMi4xMyAyLjExLTQuNjczIDMuNzkzLTcuNTcyIDUuMDFMNC41IDM2LjM4bC45NzQtMi4zMTYgMS45MjUtLjgwOGMyLjg5OC0xLjIxOCA1LjQ0LTIuOSA3LjU3LTUuMDFsMTkuNzQzLTE5LjY4Yy4yOTItLjI5Mi40MzItLjcwMi40MzItMS4xNjUgMC0uNjQ2LS4yNy0xLjQtLjc4LTIuMTIyLjI1LjE3Mi41LjM3Ny43MzcuNjE0Ljg5OC45MDUgMS4zOTYgMS45ODMgMS4zOTYgMi44NTYiIGZpbGw9IiM1NzVFNzUiIG9wYWNpdHk9Ii4xNSIvPjxwYXRoIGQ9Ik0xOC40NSAxMi44M2MwIC41LS40MDQuOTA1LS45MDQuOTA1cy0uOTA1LS40MDUtLjkwNS0uOTA0YzAtLjUuNDA3LS45MDMuOTA2LS45MDMuNSAwIC45MDQuNDA0LjkwNC45MDR6IiBmaWxsPSIjNTc1RTc1Ii8+PC9nPjwvc3ZnPg=='; + /** * Enum for pen color parameters. * @readonly @@ -235,6 +242,7 @@ class Scratch3PenBlocks { return { id: 'pen', name: 'Pen', + iconURI: iconURI, blocks: [ { opcode: 'clear', From 2ac464c53e39af1cde31973e939b07e930cb725c Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 3 Nov 2017 11:35:42 -0400 Subject: [PATCH 538/721] Prepend icon to each extension block --- src/engine/runtime.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index b9738713..eef031ac 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -421,6 +421,7 @@ class Runtime extends EventEmitter { const categoryInfo = { id: extensionInfo.id, name: extensionInfo.name, + iconURI: extensionInfo.iconURI, color1: '#FF6680', color2: '#FF4D6A', color3: '#FF3355', @@ -524,8 +525,21 @@ class Runtime extends EventEmitter { // but each `[ARG]` will need to be replaced with the number in this map instead of `args0.length`. const argsMap = {}; - blockJSON.message0 = blockInfo.text.replace(/\[(.+?)]/g, (match, placeholder) => { + blockJSON.message0 = ''; + // If an icon for the extension exists, prepend it to each block + if (categoryInfo.iconURI) { + blockJSON.message0 = '%1'; + const iconJSON = { + type: 'field_image', + src: categoryInfo.iconURI, + width: 40, + height: 40 + }; + blockJSON.args0.push(iconJSON); + } + + blockJSON.message0 += blockInfo.text.replace(/\[(.+?)]/g, (match, placeholder) => { // Sanitize the placeholder to ensure valid XML placeholder = placeholder.replace(/[<"&]/, '_'); From c1ba902ebb00b9160729d58755e3a64c3045b0c3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 3 Nov 2017 13:50:50 -0400 Subject: [PATCH 539/721] Use category info that has colors when building menu blocks --- src/engine/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index b9738713..c86516bf 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -433,7 +433,7 @@ class Runtime extends EventEmitter { for (const menuName in extensionInfo.menus) { if (extensionInfo.menus.hasOwnProperty(menuName)) { const menuItems = extensionInfo.menus[menuName]; - const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuItems, extensionInfo); + const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuItems, categoryInfo); categoryInfo.menus.push(convertedMenu); } } From 0af3de9bf0471a6598066ea1776f3dd6ccd5751b Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 3 Nov 2017 11:17:16 -0700 Subject: [PATCH 540/721] Load extensions when loading a project or sprite - Accumulate extension info while deserializing JSON - Install extensions (if any) before installing target(s) - Merged 'install' steps of the "add sprite" and "load project" paths --- src/serialization/sb2.js | 61 ++++++---- src/serialization/sb2_specmap.js | 193 +++++++++++++++++++++++++++++++ src/serialization/sb3.js | 49 ++++++-- src/virtual-machine.js | 58 ++++++---- 4 files changed, 306 insertions(+), 55 deletions(-) diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index 5225614a..b5b8030b 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -78,15 +78,16 @@ const flatten = function (blocks) { * or a list of blocks in an argument (e.g., move [pick random...]). * @param {Array.} blockList SB2 JSON-format block list. * @param {Function} getVariableId function to retreive a variable's ID based on name + * @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here. * @return {Array.} Scratch VM-format block list. */ -const parseBlockList = function (blockList, getVariableId) { +const parseBlockList = function (blockList, getVariableId, extensions) { const resultingList = []; let previousBlock = null; // For setting next. for (let i = 0; i < blockList.length; i++) { const block = blockList[i]; // eslint-disable-next-line no-use-before-define - const parsedBlock = parseBlock(block, getVariableId); + const parsedBlock = parseBlock(block, getVariableId, extensions); if (typeof parsedBlock === 'undefined') continue; if (previousBlock) { parsedBlock.parent = previousBlock.id; @@ -104,14 +105,15 @@ const parseBlockList = function (blockList, getVariableId) { * @param {!object} scripts Scripts object from SB2 JSON. * @param {!Blocks} blocks Blocks object to load parsed blocks into. * @param {Function} getVariableId function to retreive a variable's ID based on name + * @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here. */ -const parseScripts = function (scripts, blocks, getVariableId) { +const parseScripts = function (scripts, blocks, getVariableId, extensions) { for (let i = 0; i < scripts.length; i++) { const script = scripts[i]; const scriptX = script[0]; const scriptY = script[1]; const blockList = script[2]; - const parsedBlockList = parseBlockList(blockList, getVariableId); + const parsedBlockList = parseBlockList(blockList, getVariableId, extensions); if (parsedBlockList[0]) { // Adjust script coordinates to account for // larger block size in scratch-blocks. @@ -155,12 +157,14 @@ const generateVariableIdGetter = (function () { /** * Parse a single "Scratch object" and create all its in-memory VM objects. - * @param {!object} object From-JSON "Scratch object:" sprite, stage, watcher. - * @param {!Runtime} runtime Runtime object to load all structures into. - * @param {boolean} topLevel Whether this is the top-level object (stage). - * @return {?Promise} Promise that resolves to the loaded targets when ready. + * TODO: parse the "info" section, especially "savedExtensions" + * @param {!object} object - From-JSON "Scratch object:" sprite, stage, watcher. + * @param {!Runtime} runtime - Runtime object to load all structures into. + * @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here. + * @param {boolean} topLevel - Whether this is the top-level object (stage). + * @return {!Promise.>} Promise for the loaded targets when ready, or null for unsupported objects. */ -const parseScratchObject = function (object, runtime, topLevel) { +const parseScratchObject = function (object, runtime, extensions, topLevel) { if (!object.hasOwnProperty('objName')) { // Watcher/monitor - skip this object until those are implemented in VM. // @todo @@ -228,7 +232,7 @@ const parseScratchObject = function (object, runtime, topLevel) { // If included, parse any and all scripts/blocks on the object. if (object.hasOwnProperty('scripts')) { - parseScripts(object.scripts, blocks, getVariableId); + parseScripts(object.scripts, blocks, getVariableId, extensions); } if (object.hasOwnProperty('lists')) { @@ -287,7 +291,7 @@ const parseScratchObject = function (object, runtime, topLevel) { const childrenPromises = []; if (object.children) { for (let m = 0; m < object.children.length; m++) { - childrenPromises.push(parseScratchObject(object.children[m], runtime, false)); + childrenPromises.push(parseScratchObject(object.children[m], runtime, extensions, false)); } } @@ -312,31 +316,42 @@ const parseScratchObject = function (object, runtime, topLevel) { * @param {!object} json SB2-format JSON to load. * @param {!Runtime} runtime Runtime object to load all structures into. * @param {boolean=} optForceSprite If set, treat as sprite (Sprite2). - * @return {?Promise} Promise that resolves to the loaded targets when ready. + * @return {Promise.} Promise that resolves to the loaded targets when ready. */ const sb2import = function (json, runtime, optForceSprite) { - return parseScratchObject( - json, - runtime, - !optForceSprite - ); + const extensions = { + extensionIDs: new Set(), + extensionURLs: new Map() + }; + return parseScratchObject(json, runtime, extensions, !optForceSprite) + .then(targets => ({ + targets, + extensions + })); }; /** * Parse a single SB2 JSON-formatted block and its children. * @param {!object} sb2block SB2 JSON-formatted block. - * @param {Function} getVariableId function to retreive a variable's ID based on name - * @return {object} Scratch VM format block. + * @param {Function} getVariableId function to retrieve a variable's ID based on name + * @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here. + * @return {object} Scratch VM format block, or null if unsupported object. */ -const parseBlock = function (sb2block, getVariableId) { +const parseBlock = function (sb2block, getVariableId, extensions) { // First item in block object is the old opcode (e.g., 'forward:'). const oldOpcode = sb2block[0]; // Convert the block using the specMap. See sb2specmap.js. if (!oldOpcode || !specMap[oldOpcode]) { log.warn('Couldn\'t find SB2 block: ', oldOpcode); - return; + return null; } const blockMetadata = specMap[oldOpcode]; + // If the block is from an extension, record it. + const dotIndex = blockMetadata.opcode.indexOf('.'); + if (dotIndex >= 0) { + const extension = blockMetadata.opcode.substring(0, dotIndex); + extensions.extensionIDs.add(extension); + } // Block skeleton. const activeBlock = { id: uid(), // Generate a new block unique ID. @@ -373,10 +388,10 @@ const parseBlock = function (sb2block, getVariableId) { let innerBlocks; if (typeof providedArg[0] === 'object' && providedArg[0]) { // Block list occupies the input. - innerBlocks = parseBlockList(providedArg, getVariableId); + innerBlocks = parseBlockList(providedArg, getVariableId, extensions); } else { // Single block occupies the input. - innerBlocks = [parseBlock(providedArg, getVariableId)]; + innerBlocks = [parseBlock(providedArg, getVariableId, extensions)]; } let previousBlock = null; for (let j = 0; j < innerBlocks.length; j++) { diff --git a/src/serialization/sb2_specmap.js b/src/serialization/sb2_specmap.js index 1b697934..6257bc1d 100644 --- a/src/serialization/sb2_specmap.js +++ b/src/serialization/sb2_specmap.js @@ -21,6 +21,24 @@ * properties. By hand, I matched the opcode name to the 3.0 opcode. * Finally, I filled in the expected arguments as below. */ + +/** + * @typedef {object} SB2SpecMap_blockInfo + * @property {string} opcode - the Scratch 3.0 block opcode. Use 'extensionID.opcode' for extension opcodes. + * @property {Array.} argMap - metadata for this block's arguments. + */ + +/** + * @typedef {object} SB2SpecMap_argInfo + * @property {string} type - the type of this arg (such as 'input' or 'field') + * @property {string} inputOp - the scratch-blocks shadow type for this arg + * @property {string} inputName - the name this argument will take when provided to the block implementation + */ + +/** + * Mapping of Scratch 2.0 opcode to Scratch 3.0 block metadata. + * @type {object.} + */ const specMap = { 'forward:': { opcode: 'motion_movesteps', @@ -1376,4 +1394,179 @@ const specMap = { argMap: [] } }; + +/** + * Add to the specMap entries for an opcode from a Scratch 2.0 extension. Two entries will be made with the same + * metadata; this is done to support projects saved by both older and newer versions of the Scratch 2.0 editor. + * @param {string} sb2Extension - the Scratch 2.0 name of the extension + * @param {string} sb2Opcode - the Scratch 2.0 opcode + * @param {SB2SpecMap_blockInfo} blockInfo - the Scratch 3.0 block info + */ +const addExtensionOp = function (sb2Extension, sb2Opcode, blockInfo) { + /** + * This string separates the name of an extension and the name of an opcode in more recent Scratch 2.0 projects. + * Earlier projects used '.' as a separator, up until we added the 'LEGO WeDo 2.0' extension... + * @type {string} + */ + const sep = '\u001F'; // Unicode Unit Separator + + // make one entry for projects saved by recent versions of the Scratch 2.0 editor + specMap[`${sb2Extension}${sep}${sb2Opcode}`] = blockInfo; + + // make a second for projects saved by older versions of the Scratch 2.0 editor + specMap[`${sb2Extension}.${sb2Opcode}`] = blockInfo; +}; + +const weDo2 = 'LEGO WeDo 2.0'; + +addExtensionOp(weDo2, 'motorOnFor', { + opcode: 'wedo2.motorOnFor', + argMap: [ + { + type: 'input', + inputOp: 'text', + inputName: 'MOTOR_ID' + }, + { + type: 'input', + inputOp: 'math_number', + inputName: 'DURATION' + } + ] +}); + +addExtensionOp(weDo2, 'motorOn', { + opcode: 'wedo2.motorOn', + argMap: [ + { + type: 'input', + inputOp: 'text', + inputName: 'MOTOR_ID' + } + ] +}); + +addExtensionOp(weDo2, 'motorOff', { + opcode: 'wedo2.motorOff', + argMap: [ + { + type: 'input', + inputOp: 'text', + inputName: 'MOTOR_ID' + } + ] +}); + +addExtensionOp(weDo2, 'startMotorPower', { + opcode: 'wedo2.startMotorPower', + argMap: [ + { + type: 'input', + inputOp: 'text', + inputName: 'MOTOR_ID' + }, + { + type: 'input', + inputOp: 'math_number', + inputName: 'POWER' + } + ] +}); + +addExtensionOp(weDo2, 'setMotorDirection', { + opcode: 'wedo2.setMotorDirection', + argMap: [ + { + type: 'input', + inputOp: 'text', + inputName: 'MOTOR_ID' + }, + { + type: 'input', + inputOp: 'text', + inputName: 'DIRECTION' + } + ] +}); + +addExtensionOp(weDo2, 'setLED', { + opcode: 'wedo2.setLightHue', + argMap: [ + { + type: 'input', + inputOp: 'math_number', + inputName: 'HUE' + } + ] +}); + +addExtensionOp(weDo2, 'playNote', { + opcode: 'wedo2.playNoteFor', + argMap: [ + { + type: 'input', + inputOp: 'math_number', + inputName: 'NOTE' + }, + { + type: 'input', + inputOp: 'math_number', + inputName: 'DURATION' + } + ] +}); + +addExtensionOp(weDo2, 'whenDistance', { + opcode: 'wedo2.whenDistance', + argMap: [ + { + type: 'input', + inputOp: 'text', + inputName: 'OP' + }, + { + type: 'input', + inputOp: 'math_number', + inputName: 'REFERENCE' + } + ] +}); + +addExtensionOp(weDo2, 'whenTilted', { + opcode: 'wedo2.whenTilted', + argMap: [ + { + type: 'input', + inputOp: 'text', + inputName: 'DIRECTION' + } + ] +}); + +addExtensionOp(weDo2, 'getDistance', { + opcode: 'wedo2.motorOn' +}); + +addExtensionOp(weDo2, 'isTilted', { + opcode: 'wedo2.motorOn', + argMap: [ + { + type: 'input', + inputOp: 'text', + inputName: 'DIRECTION' + } + ] +}); + +addExtensionOp(weDo2, 'getTilt', { + opcode: 'getTiltAngle', + argMap: [ + { + type: 'input', + inputOp: 'text', + inputName: 'DIRECTION' + } + ] +}); + module.exports = specMap; diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index d73a472f..13bdd2e7 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -13,6 +13,18 @@ const List = require('../engine/list'); const {loadCostume} = require('../import/load-costume.js'); const {loadSound} = require('../import/load-sound.js'); +/** + * @typedef {object} ImportedProject + * @property {Array.} targets - the imported Scratch 3.0 target objects. + * @property {ImportedExtensionsInfo} extensionsInfo - the ID of each extension actually used by this project. + */ + +/** + * @typedef {object} ImportedExtensionsInfo + * @property {Set.} extensionIDs - the ID of each extension actually in use by blocks in this project. + * @property {Map.} extensionURLs - map of ID => URL from project metadata. May not match extensionIDs. + */ + /** * Serializes the specified VM runtime. * @param {!Runtime} runtime VM runtime instance to be serialized. @@ -41,13 +53,14 @@ const serialize = function (runtime) { * Parse a single "Scratch object" and create all its in-memory VM objects. * @param {!object} object From-JSON "Scratch object:" sprite, stage, watcher. * @param {!Runtime} runtime Runtime object to load all structures into. - * @return {?Target} Target created (stage or sprite). + * @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here. + * @return {!Promise.} Promise for the target created (stage or sprite), or null for unsupported objects. */ -const parseScratchObject = function (object, runtime) { +const parseScratchObject = function (object, runtime, extensions) { if (!object.hasOwnProperty('name')) { // Watcher/monitor - skip this object until those are implemented in VM. // @todo - return; + return Promise.resolve(null); } // Blocks container for this object. const blocks = new Blocks(); @@ -61,7 +74,14 @@ const parseScratchObject = function (object, runtime) { } if (object.hasOwnProperty('blocks')) { for (const blockId in object.blocks) { - blocks.createBlock(object.blocks[blockId]); + const blockJSON = object.blockType[blockId]; + blocks.createBlock(blockJSON); + + const dotIndex = blockJSON.opcode.indexOf('.'); + if (dotIndex >= 0) { + const extensionId = blockJSON.opcode.substring(0, dotIndex); + extensions.extensionIDs.add(extensionId); + } } // console.log(blocks); } @@ -155,14 +175,23 @@ const parseScratchObject = function (object, runtime) { }; /** - * Deserializes the specified representation of a VM runtime and loads it into - * the provided runtime instance. - * @param {object} json JSON representation of a VM runtime. - * @param {Runtime} runtime Runtime instance - * @returns {Promise} Promise that resolves to the list of targets after the project is deserialized + * Deserialize the specified representation of a VM runtime and loads it into the provided runtime instance. + * TODO: parse extension info (also, design extension info storage...) + * @param {object} json - JSON representation of a VM runtime. + * @param {Runtime} runtime - Runtime instance + * @returns {Promise.} Promise that resolves to the list of targets after the project is deserialized */ const deserialize = function (json, runtime) { - return Promise.all((json.targets || []).map(target => parseScratchObject(target, runtime))); + const extensions = { + extensionIDs: new Set(), + extensionURLs: new Map() + }; + return Promise.all( + (json.targets || []).map(target => parseScratchObject(target, runtime, extensions)) + ).then(targets => ({ + targets, + extensions + })); }; module.exports = { diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 6f065841..a1dac608 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -33,7 +33,7 @@ class VirtualMachine extends EventEmitter { /** * The "currently editing"/selected target ID for the VM. * Block events from any Blockly workspace are routed to this target. - * @type {!string} + * @type {Target} */ this.editingTarget = null; // Runtime emits are passed along as VM emits. @@ -228,19 +228,41 @@ class VirtualMachine extends EventEmitter { deserializer = sb2; } - return deserializer.deserialize(json, this.runtime).then(targets => { - this.clear(); - for (let n = 0; n < targets.length; n++) { - if (targets[n] !== null) { - this.runtime.targets.push(targets[n]); - targets[n].updateAllDrawableProperties(); - } + return deserializer.deserialize(json, this.runtime) + .then(({targets, extensions}) => + this.installTargets(targets, extensions, true)); + } + + /** + * Install `deserialize` results: zero or more targets after the extensions (if any) used by those targets. + * @param {Array.} targets - the targets to be installed + * @param {ImportedExtensionsInfo} extensions - metadata about extensions used by these targets + * @param {boolean} wholeProject - set to true if installing a whole project, as opposed to a single sprite. + */ + installTargets (targets, extensions, wholeProject) { + const extensionPromises = []; + extensions.extensionIDs.forEach(extensionID => { + if (!this.extensionManager.isExtensionLoaded(extensionID)) { + const extensionURL = extensions.extensionURLs.get(extensionID) || extensionID; + extensionPromises.push(this.extensionManager.loadExtensionURL(extensionURL)); } + }); + + targets = targets.filter(target => !!target); + + Promise.all(extensionPromises).then(() => { + if (wholeProject) { + this.clear(); + } + targets.forEach(target => { + this.runtime.targets.push(target); + (/** @type RenderedTarget */ target).updateAllDrawableProperties(); + }); // Select the first target for editing, e.g., the first sprite. - if (this.runtime.targets.length > 1) { - this.editingTarget = this.runtime.targets[1]; + if (wholeProject && (targets.length > 1)) { + this.editingTarget = targets[1]; } else { - this.editingTarget = this.runtime.targets[0]; + this.editingTarget = targets[0]; } // Update the VM user's knowledge of targets and blocks on the workspace. @@ -267,17 +289,9 @@ class VirtualMachine extends EventEmitter { return; } - // Select new sprite. - return sb2.deserialize(json, this.runtime, true).then(targets => { - this.runtime.targets.push(targets[0]); - this.editingTarget = targets[0]; - this.editingTarget.updateAllDrawableProperties(); - - // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); - }); + return sb2.deserialize(json, this.runtime, true) + .then(({targets, extensions}) => + this.installTargets(targets, extensions, false)); } /** From c88a8fa55a2a79fd78f9957a22c99dc6d765ef93 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 3 Nov 2017 11:42:20 -0700 Subject: [PATCH 541/721] Fix tests & a typo --- src/serialization/sb3.js | 2 +- test/integration/import_sb2.js | 2 +- test/integration/sb3-roundtrip.js | 2 +- test/unit/serialization_sb2.js | 4 ++-- test/unit/serialization_sb3.js | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 13bdd2e7..426f1004 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -74,7 +74,7 @@ const parseScratchObject = function (object, runtime, extensions) { } if (object.hasOwnProperty('blocks')) { for (const blockId in object.blocks) { - const blockJSON = object.blockType[blockId]; + const blockJSON = object.blocks[blockId]; blocks.createBlock(blockJSON); const dotIndex = blockJSON.opcode.indexOf('.'); diff --git a/test/integration/import_sb2.js b/test/integration/import_sb2.js index f9a0d2d0..ec553dd9 100644 --- a/test/integration/import_sb2.js +++ b/test/integration/import_sb2.js @@ -21,7 +21,7 @@ test('default', t => { // Create runtime instance & load SB2 into it const rt = new runtime(); rt.attachStorage(makeTestStorage()); - sb2.deserialize(json, rt).then(targets => { + sb2.deserialize(json, rt).then(({targets}) => { // Test t.type(file, 'string'); t.type(json, 'object'); diff --git a/test/integration/sb3-roundtrip.js b/test/integration/sb3-roundtrip.js index 058169a8..5704e33b 100644 --- a/test/integration/sb3-roundtrip.js +++ b/test/integration/sb3-roundtrip.js @@ -103,7 +103,7 @@ test('sb3-roundtrip', t => { return sb3.deserialize(serializedState, runtime2); }); - return serializeAndDeserialize.then(targets => { + return serializeAndDeserialize.then(({targets}) => { runtime2.targets = targets; testRuntimeState('copy', runtime2); }); diff --git a/test/unit/serialization_sb2.js b/test/unit/serialization_sb2.js index 68535b40..d5aaa024 100644 --- a/test/unit/serialization_sb2.js +++ b/test/unit/serialization_sb2.js @@ -20,7 +20,7 @@ test('default', t => { // Create runtime instance & load SB2 into it const rt = new Runtime(); - sb2.deserialize(json, rt).then(targets => { + sb2.deserialize(json, rt).then(({targets}) => { // Test t.type(file, 'string'); t.type(json, 'object'); @@ -60,7 +60,7 @@ test('data scoping', t => { // Create runtime instance & load SB2 into it const rt = new Runtime(); - sb2.deserialize(json, rt).then(targets => { + sb2.deserialize(json, rt).then(({targets}) => { const globalVariableIds = Object.keys(targets[0].variables); const localVariableIds = Object.keys(targets[1].variables); t.equal(targets[0].variables[globalVariableIds[0]].name, 'foo'); diff --git a/test/unit/serialization_sb3.js b/test/unit/serialization_sb3.js index a43ce204..5c2b8a1e 100644 --- a/test/unit/serialization_sb3.js +++ b/test/unit/serialization_sb3.js @@ -14,8 +14,8 @@ test('serialize', t => { test('deserialize', t => { const vm = new VirtualMachine(); - sb3.deserialize('', vm.runtime).then(targets => { - // @todo Analyize + sb3.deserialize('', vm.runtime).then(({targets}) => { + // @todo Analyze t.type(targets, 'object'); t.end(); }); From 51dcfb954d037378c5cf5d31c2425ad0cd028ba1 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 3 Nov 2017 12:50:37 -0700 Subject: [PATCH 542/721] Pen integration test: don't pre-load pen extension --- src/virtual-machine.js | 3 ++- test/integration/pen.js | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index a1dac608..6971e450 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -238,6 +238,7 @@ class VirtualMachine extends EventEmitter { * @param {Array.} targets - the targets to be installed * @param {ImportedExtensionsInfo} extensions - metadata about extensions used by these targets * @param {boolean} wholeProject - set to true if installing a whole project, as opposed to a single sprite. + * @returns {Promise} resolved once targets have been installed */ installTargets (targets, extensions, wholeProject) { const extensionPromises = []; @@ -250,7 +251,7 @@ class VirtualMachine extends EventEmitter { targets = targets.filter(target => !!target); - Promise.all(extensionPromises).then(() => { + return Promise.all(extensionPromises).then(() => { if (wholeProject) { this.clear(); } diff --git a/test/integration/pen.js b/test/integration/pen.js index 025735ba..7df798e7 100644 --- a/test/integration/pen.js +++ b/test/integration/pen.js @@ -48,7 +48,6 @@ test('pen', t => { vm.setCompatibilityMode(false); vm.setTurboMode(false); vm.loadProject(project) - .then(() => vm.extensionManager.loadExtensionURL('pen')) /** @TODO: loadProject should load extensions */ .then(() => { vm.greenFlag(); From 0ad03f8d70619ddba8b9fe779d119a2c6c803a87 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 3 Nov 2017 13:48:05 -0700 Subject: [PATCH 543/721] Actually finish SB2 specMap for WeDo 2.0 Apparently I took a break at the wrong time and forgot to come back to this... :/ --- src/serialization/sb2_specmap.js | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/serialization/sb2_specmap.js b/src/serialization/sb2_specmap.js index 6257bc1d..cf5be1c8 100644 --- a/src/serialization/sb2_specmap.js +++ b/src/serialization/sb2_specmap.js @@ -1424,7 +1424,7 @@ addExtensionOp(weDo2, 'motorOnFor', { argMap: [ { type: 'input', - inputOp: 'text', + inputOp: 'wedo2.menu.motorID', inputName: 'MOTOR_ID' }, { @@ -1440,7 +1440,7 @@ addExtensionOp(weDo2, 'motorOn', { argMap: [ { type: 'input', - inputOp: 'text', + inputOp: 'wedo2.menu.motorID', inputName: 'MOTOR_ID' } ] @@ -1451,7 +1451,7 @@ addExtensionOp(weDo2, 'motorOff', { argMap: [ { type: 'input', - inputOp: 'text', + inputOp: 'wedo2.menu.motorID', inputName: 'MOTOR_ID' } ] @@ -1462,7 +1462,7 @@ addExtensionOp(weDo2, 'startMotorPower', { argMap: [ { type: 'input', - inputOp: 'text', + inputOp: 'wedo2.menu.motorID', inputName: 'MOTOR_ID' }, { @@ -1478,12 +1478,12 @@ addExtensionOp(weDo2, 'setMotorDirection', { argMap: [ { type: 'input', - inputOp: 'text', + inputOp: 'wedo2.menu.motorID', inputName: 'MOTOR_ID' }, { type: 'input', - inputOp: 'text', + inputOp: 'wedo2.menu.motorDirection', inputName: 'DIRECTION' } ] @@ -1521,7 +1521,7 @@ addExtensionOp(weDo2, 'whenDistance', { argMap: [ { type: 'input', - inputOp: 'text', + inputOp: 'wedo2.menu.lessMore', inputName: 'OP' }, { @@ -1537,33 +1537,34 @@ addExtensionOp(weDo2, 'whenTilted', { argMap: [ { type: 'input', - inputOp: 'text', + inputOp: 'wedo2.menu.tiltDirectionAny', inputName: 'DIRECTION' } ] }); addExtensionOp(weDo2, 'getDistance', { - opcode: 'wedo2.motorOn' + opcode: 'wedo2.getDistance', + argMap: [] }); addExtensionOp(weDo2, 'isTilted', { - opcode: 'wedo2.motorOn', + opcode: 'wedo2.isTilted', argMap: [ { type: 'input', - inputOp: 'text', + inputOp: 'wedo2.menu.tiltDirectionAny', inputName: 'DIRECTION' } ] }); addExtensionOp(weDo2, 'getTilt', { - opcode: 'getTiltAngle', + opcode: 'wedo2.getTiltAngle', argMap: [ { type: 'input', - inputOp: 'text', + inputOp: 'wedo2.menu.tiltDirection', inputName: 'DIRECTION' } ] From 4e24a3f3807bd30dca6785172408b66ba4bf2c61 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Fri, 3 Nov 2017 11:52:42 -0400 Subject: [PATCH 544/721] Constant time check if thread is in doneThreads Remove indexOf tests for thread existence in doneThreads. Maintain a list of null and thread objects mirroring the index position of threads that are done in runtime.threads. Filter out null values after filtering out done threads from runtime.threads. This has a small side effect that threads that normally became DONE and were added to doneThreads before being removed or restarted will not be in doneThreads in this version. Restarted threads (_restartThread) do not appear in this version but new copies will be in Runtime this.threads supporting glow and monitor updates. Removed threads (_removeThread) do not appear in this version if they are removed after they were seen as DONE by the prior version. Threads removed before they are seen as DONE do not appear in doneThreads in the prior or this version. Threads that are removed before normally becoming DONE do not appear in doneThreads in either case. Threads that are restarted before the loop checks if the thread is done do not appear in doneThreads in either case. --- src/engine/sequencer.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js index 0a126d67..9664290c 100644 --- a/src/engine/sequencer.js +++ b/src/engine/sequencer.js @@ -38,7 +38,7 @@ class Sequencer { let numActiveThreads = Infinity; // Whether `stepThreads` has run through a full single tick. let ranFirstTick = false; - const doneThreads = []; + const doneThreads = this.runtime.threads.map(() => null); // Conditions for continuing to stepping threads: // 1. We must have threads in the list, and some must be active. // 2. Time elapsed must be less than WORK_TIME. @@ -54,11 +54,13 @@ class Sequencer { if (activeThread.stack.length === 0 || activeThread.status === Thread.STATUS_DONE) { // Finished with this thread. - if (doneThreads.indexOf(activeThread) < 0) { - doneThreads.push(activeThread); - } + doneThreads[i] = activeThread; continue; } + // A thread was removed, added or this thread was restarted. + if (doneThreads[i] !== null) { + doneThreads[i] = null; + } if (activeThread.status === Thread.STATUS_YIELD_TICK && !ranFirstTick) { // Clear single-tick yield from the last call of `stepThreads`. @@ -82,13 +84,13 @@ class Sequencer { ranFirstTick = true; } // Filter inactive threads from `this.runtime.threads`. - this.runtime.threads = this.runtime.threads.filter(thread => { - if (doneThreads.indexOf(thread) > -1) { + this.runtime.threads = this.runtime.threads.filter((thread, i) => { + if (doneThreads[i] === null) { return false; } return true; }); - return doneThreads; + return doneThreads.filter(Boolean); } /** From 6d82c0f115713ae729dfadc024ddf6d70e21b25f Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Fri, 3 Nov 2017 16:24:12 -0400 Subject: [PATCH 545/721] In place filter threads at end of stepThreads Skip a large array allocation by filtering done threads in place with a for loop. --- src/engine/sequencer.js | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js index 9664290c..f4edc966 100644 --- a/src/engine/sequencer.js +++ b/src/engine/sequencer.js @@ -84,13 +84,28 @@ class Sequencer { ranFirstTick = true; } // Filter inactive threads from `this.runtime.threads`. - this.runtime.threads = this.runtime.threads.filter((thread, i) => { + numActiveThreads = 0; + for (let i = 0; i < this.runtime.threads.length; i++) { + const thread = this.runtime.threads[i]; if (doneThreads[i] === null) { - return false; + this.runtime.threads[numActiveThreads] = thread; + numActiveThreads++; } - return true; - }); - return doneThreads.filter(Boolean); + } + this.runtime.threads.length = numActiveThreads; + + // Filter undefined and null values from `doneThreads`. + let numDoneThreads = 0; + for (let i = 0; i < doneThreads.length; i++) { + const maybeThread = doneThreads[i]; + if (maybeThread !== null) { + doneThreads[numDoneThreads] = maybeThread; + numDoneThreads++; + } + } + doneThreads.length = numDoneThreads; + + return doneThreads; } /** From 8616390fabcfaa686a746a1359d7083d6261b166 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 3 Nov 2017 21:24:47 -0400 Subject: [PATCH 546/721] Add back pen transparency from color number --- src/blocks/scratch3_pen.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/blocks/scratch3_pen.js b/src/blocks/scratch3_pen.js index 7de5e51a..2a0bd490 100644 --- a/src/blocks/scratch3_pen.js +++ b/src/blocks/scratch3_pen.js @@ -451,7 +451,11 @@ class Scratch3PenBlocks { penState.color = (hsv.h / 360) * 100; penState.saturation = hsv.s * 100; penState.brightness = hsv.v * 100; - penState.transparency = 0; + if (rgb.hasOwnProperty('a')) { + penState.transparency = 100 * (1 - rgb.a / 255.0); + } else { + penState.transparency = 0; + } // Set the legacy "shade" value the same way scratch 2 did. penState._shade = penState.brightness / 2; From caa9b97520db78de8f06d2ff2eaa098988f8d952 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Sat, 4 Nov 2017 10:10:17 -0400 Subject: [PATCH 547/721] Fix linting --- src/blocks/scratch3_pen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/scratch3_pen.js b/src/blocks/scratch3_pen.js index 2a0bd490..40721512 100644 --- a/src/blocks/scratch3_pen.js +++ b/src/blocks/scratch3_pen.js @@ -452,7 +452,7 @@ class Scratch3PenBlocks { penState.saturation = hsv.s * 100; penState.brightness = hsv.v * 100; if (rgb.hasOwnProperty('a')) { - penState.transparency = 100 * (1 - rgb.a / 255.0); + penState.transparency = 100 * (1 - (rgb.a / 255.0)); } else { penState.transparency = 0; } From 14087ef24fe7ae87570272a77bde45753182687b Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 6 Nov 2017 10:00:13 -0500 Subject: [PATCH 548/721] Add wedo block icon --- src/blocks/scratch3_wedo2.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/blocks/scratch3_wedo2.js b/src/blocks/scratch3_wedo2.js index abc09e5d..37011517 100644 --- a/src/blocks/scratch3_wedo2.js +++ b/src/blocks/scratch3_wedo2.js @@ -3,6 +3,12 @@ const BlockType = require('../extension-support/block-type'); const color = require('../util/color'); const log = require('../util/log'); +/** + * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI. + * @type {string} + */ +const iconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+d2VkbzItYmxvY2staWNvbjwvdGl0bGU+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNMzUuMzEzIDEwLjQ2N0gzMi4wOVY4Ljg2NWMwLS4yMjMuMTgtLjQwNC40MDUtLjQwNGgyLjQxMmMuMjI0IDAgLjQwNi4xODIuNDA2LjQwNXYxLjYwMnpNMzAuNDc3IDEwLjQ2N2gtMy4yMjRWOC44NjVjMC0uMjIzLjE4My0uNDA0LjQwNy0uNDA0aDIuNDFjLjIyNiAwIC40MDcuMTgyLjQwNy40MDV2MS42MDJ6TTI1LjY0IDEwLjQ2N0gyMi40MlY4Ljg2NWMwLS4yMjMuMTgyLS40MDQuNDA2LS40MDRoMi40MWMuMjI2IDAgLjQwNy4xODIuNDA3LjQwNXYxLjYwMnpNMjAuODA2IDEwLjQ2N2gtMy4yMjRWOC44NjVjMC0uMjIzLjE4Mi0uNDA0LjQwNi0uNDA0SDIwLjRjLjIyNCAwIC40MDYuMTgyLjQwNi40MDV2MS42MDJ6TTE1Ljk3IDEwLjQ2N2gtMy4yMjRWOC44NjVjMC0uMjIzLjE4Mi0uNDA0LjQwNy0uNDA0aDIuNDFjLjIyNiAwIC40MDcuMTgyLjQwNy40MDV2MS42MDJ6TTExLjEzNSAxMC40NjdINy45MVY4Ljg2NWMwLS4yMjMuMTgzLS40MDQuNDA3LS40MDRoMi40MTJjLjIyMyAwIC40MDUuMTgyLjQwNS40MDV2MS42MDJ6IiBzdHJva2U9IiM2Rjc4OTMiIGZpbGw9IiNGRkYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0zNy43MyAxMC40NjdINi4zYy0yLjY3IDAtNC44MzYgMi4xNTMtNC44MzYgNC44MDh2My4yMDVoMzcuMDczdi03LjIxYzAtLjQ0NC0uMzYyLS44MDMtLjgwNy0uODAzeiIgc3Ryb2tlPSIjNkY3ODkzIiBmaWxsPSIjRkZGIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMzguMTM0IDMwLjk4SDEuODY3Yy0uMjI0IDAtLjQwMy0uMTgtLjQwMy0uNFYxNi4yMzZoMzIuNzFjLjczIDAgMS40My4yODcgMS45NDUuOC41MTUuNTE0IDEuMjE1LjgwMiAxLjk0NC44MDJoLjQ3M3YxMi43NGMwIC4yMi0uMTguNC0uNDAzLjR6IiBzdHJva2U9IiM2Rjc4OTMiIGZpbGw9IiNFNkU3RTgiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIHN0cm9rZT0iIzZGNzg5MyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBkPSJNMzQuODMgMTYuMjM3bC40ODMtMi41NjVoMy4yMjMiLz48cGF0aCBkPSJNMzguNTM2IDExLjI2OFYzMC41OGMwIC4yMi0uMTguNC0uNDAzLjRIMS44NjZjLS4yMiAwLS40MDMtLjE4LS40MDMtLjR2LTEuMjAzaDM0LjI4MmMuNjUgMCAxLjE4LS41MjQgMS4xOC0xLjE3M1YxMC40NjdoLjgwNWMuNDQ2IDAgLjgwNi4zNi44MDYuOHoiIHN0cm9rZT0iIzZGNzg5MyIgZmlsbD0iIzZGNzg5MyIgb3BhY2l0eT0iLjE1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMTEuNTM4IDE2LjI4aDIwLjE0OGMuMjIyIDAgLjQwMy4xOC40MDMuNHY2LjUyN2MwIC4yMjItLjE4Mi40LS40MDQuNEgxMS41MzhjLS4yMjMgMC0uNDA0LS4xNzgtLjQwNC0uNFYxNi42OGMwLS4yMi4xOC0uNC40MDQtLjQiIGZpbGw9IiNFNkU3RTgiLz48cGF0aCBkPSJNMTEuNTM4IDE2LjI4aDIwLjE0OGMuMjIyIDAgLjQwMy4xOC40MDMuNHY2LjUyN2MwIC4yMjItLjE4Mi40LS40MDQuNEgxMS41MzhjLS4yMjMgMC0uNDA0LS4xNzgtLjQwNC0uNFYxNi42OGMwLS4yMi4xOC0uNC40MDQtLjR6IiBzdHJva2U9IiM2Rjc4OTMiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0zMi4wOSAxNi4yOHY2LjkyN2MwIC4yMjItLjE4LjQtLjQwNC40aC0yMC4xNWMtLjIyIDAtLjQtLjE4LS40LS40di0xLjJoMTguMTZjLjY1MyAwIDEuMTgtLjUyNiAxLjE4LTEuMTc0VjE2LjI4aDEuNjEzeiIgc3Ryb2tlPSIjNkY3ODkzIiBmaWxsPSIjNkU3NzkyIiBvcGFjaXR5PSIuMTUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0zMC40NzcgMTYuMjhoLTMuMjI0di0xLjYwNGMwLS4yMjMuMTgzLS40MDQuNDA3LS40MDRoMi40MWMuMjI2IDAgLjQwNy4xOC40MDcuNDA0djEuNjAzek0xNS45NyAxNi4yOGgtMy4yMjR2LTEuNjA0YzAtLjIyMy4xODItLjQwNC40MDctLjQwNGgyLjQxYy4yMjYgMCAuNDA3LjE4LjQwNy40MDR2MS42MDN6TTI1LjY0IDE2LjI4SDIyLjQydi0xLjYwNGMwLS4yMjMuMTgyLS40MDQuNDA2LS40MDRoMi40MWMuMjI2IDAgLjQwNy4xOC40MDcuNDA0djEuNjAzek0yMC44MDYgMTYuMjhoLTMuMjI0di0xLjYwNGMwLS4yMjMuMTgyLS40MDQuNDA2LS40MDRIMjAuNGMuMjI0IDAgLjQwNi4xOC40MDYuNDA0djEuNjAzeiIgc3Ryb2tlPSIjNkY3ODkzIiBmaWxsPSIjRTZFN0U4IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMTguNTU3IDE5LjkxYzAgMS4wMjUtLjgzNyAxLjg1Ny0xLjg3IDEuODU3LTEuMDMgMC0xLjg2Ny0uODMyLTEuODY3LTEuODU4IDAtMS4wMjcuODM3LTEuODU4IDEuODY4LTEuODU4IDEuMDMyIDAgMS44Ny44MyAxLjg3IDEuODU3ek0yMy40OCAxOS45MWMwIDEuMDI1LS44MzYgMS44NTctMS44NjggMS44NTdzLTEuODctLjgzMi0xLjg3LTEuODU4YzAtMS4wMjcuODM4LTEuODU4IDEuODctMS44NThzMS44NjguODMgMS44NjggMS44NTd6TTI4LjQwNCAxOS45MWMwIDEuMDI1LS44MzcgMS44NTctMS44NjggMS44NTctMS4wMzIgMC0xLjg3LS44MzItMS44Ny0xLjg1OCAwLTEuMDI3LjgzOC0xLjg1OCAxLjg3LTEuODU4IDEuMDMgMCAxLjg2OC44MyAxLjg2OCAxLjg1N3oiIHN0cm9rZT0iIzZGNzg5MyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTE4LjU1NyAxOS45MjJjMCAxLjAyNi0uODM3IDEuODU4LTEuODcgMS44NTgtMS4wMyAwLTEuODY3LS44MzItMS44NjctMS44NTggMC0xLjAyNS44MzctMS44NTcgMS44NjgtMS44NTcgMS4wMzIgMCAxLjg3LjgzMiAxLjg3IDEuODU3TTIzLjQ4IDE5LjkyMmMwIDEuMDI2LS44MzYgMS44NTgtMS44NjggMS44NThzLTEuODctLjgzMi0xLjg3LTEuODU4YzAtMS4wMjUuODM4LTEuODU3IDEuODctMS44NTdzMS44NjguODMyIDEuODY4IDEuODU3TTI4LjQwNCAxOS45MjJjMCAxLjAyNi0uODM3IDEuODU4LTEuODY4IDEuODU4LTEuMDMyIDAtMS44Ny0uODMyLTEuODctMS44NTggMC0xLjAyNS44MzgtMS44NTcgMS44Ny0xLjg1NyAxLjAzIDAgMS44NjguODMyIDEuODY4IDEuODU3IiBmaWxsPSIjNkY3ODkzIiBvcGFjaXR5PSIuNSIvPjwvZz48L3N2Zz4='; + /** * Manage power, direction, and timers for one WeDo 2.0 motor. */ @@ -407,6 +413,7 @@ class Scratch3WeDo2Blocks { return { id: Scratch3WeDo2Blocks.EXTENSION_ID, name: 'WeDo 2.0', + iconURI: iconURI, blocks: [ { opcode: 'motorOnFor', From 323fde3859c066f0740a23b653e32dbf15e0b077 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 6 Nov 2017 10:10:41 -0500 Subject: [PATCH 549/721] Appease the linter --- src/blocks/scratch3_wedo2.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/blocks/scratch3_wedo2.js b/src/blocks/scratch3_wedo2.js index 37011517..b8bb02dd 100644 --- a/src/blocks/scratch3_wedo2.js +++ b/src/blocks/scratch3_wedo2.js @@ -7,6 +7,7 @@ const log = require('../util/log'); * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI. * @type {string} */ +// eslint-disable-next-line max-len const iconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+d2VkbzItYmxvY2staWNvbjwvdGl0bGU+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNMzUuMzEzIDEwLjQ2N0gzMi4wOVY4Ljg2NWMwLS4yMjMuMTgtLjQwNC40MDUtLjQwNGgyLjQxMmMuMjI0IDAgLjQwNi4xODIuNDA2LjQwNXYxLjYwMnpNMzAuNDc3IDEwLjQ2N2gtMy4yMjRWOC44NjVjMC0uMjIzLjE4My0uNDA0LjQwNy0uNDA0aDIuNDFjLjIyNiAwIC40MDcuMTgyLjQwNy40MDV2MS42MDJ6TTI1LjY0IDEwLjQ2N0gyMi40MlY4Ljg2NWMwLS4yMjMuMTgyLS40MDQuNDA2LS40MDRoMi40MWMuMjI2IDAgLjQwNy4xODIuNDA3LjQwNXYxLjYwMnpNMjAuODA2IDEwLjQ2N2gtMy4yMjRWOC44NjVjMC0uMjIzLjE4Mi0uNDA0LjQwNi0uNDA0SDIwLjRjLjIyNCAwIC40MDYuMTgyLjQwNi40MDV2MS42MDJ6TTE1Ljk3IDEwLjQ2N2gtMy4yMjRWOC44NjVjMC0uMjIzLjE4Mi0uNDA0LjQwNy0uNDA0aDIuNDFjLjIyNiAwIC40MDcuMTgyLjQwNy40MDV2MS42MDJ6TTExLjEzNSAxMC40NjdINy45MVY4Ljg2NWMwLS4yMjMuMTgzLS40MDQuNDA3LS40MDRoMi40MTJjLjIyMyAwIC40MDUuMTgyLjQwNS40MDV2MS42MDJ6IiBzdHJva2U9IiM2Rjc4OTMiIGZpbGw9IiNGRkYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0zNy43MyAxMC40NjdINi4zYy0yLjY3IDAtNC44MzYgMi4xNTMtNC44MzYgNC44MDh2My4yMDVoMzcuMDczdi03LjIxYzAtLjQ0NC0uMzYyLS44MDMtLjgwNy0uODAzeiIgc3Ryb2tlPSIjNkY3ODkzIiBmaWxsPSIjRkZGIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMzguMTM0IDMwLjk4SDEuODY3Yy0uMjI0IDAtLjQwMy0uMTgtLjQwMy0uNFYxNi4yMzZoMzIuNzFjLjczIDAgMS40My4yODcgMS45NDUuOC41MTUuNTE0IDEuMjE1LjgwMiAxLjk0NC44MDJoLjQ3M3YxMi43NGMwIC4yMi0uMTguNC0uNDAzLjR6IiBzdHJva2U9IiM2Rjc4OTMiIGZpbGw9IiNFNkU3RTgiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIHN0cm9rZT0iIzZGNzg5MyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBkPSJNMzQuODMgMTYuMjM3bC40ODMtMi41NjVoMy4yMjMiLz48cGF0aCBkPSJNMzguNTM2IDExLjI2OFYzMC41OGMwIC4yMi0uMTguNC0uNDAzLjRIMS44NjZjLS4yMiAwLS40MDMtLjE4LS40MDMtLjR2LTEuMjAzaDM0LjI4MmMuNjUgMCAxLjE4LS41MjQgMS4xOC0xLjE3M1YxMC40NjdoLjgwNWMuNDQ2IDAgLjgwNi4zNi44MDYuOHoiIHN0cm9rZT0iIzZGNzg5MyIgZmlsbD0iIzZGNzg5MyIgb3BhY2l0eT0iLjE1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMTEuNTM4IDE2LjI4aDIwLjE0OGMuMjIyIDAgLjQwMy4xOC40MDMuNHY2LjUyN2MwIC4yMjItLjE4Mi40LS40MDQuNEgxMS41MzhjLS4yMjMgMC0uNDA0LS4xNzgtLjQwNC0uNFYxNi42OGMwLS4yMi4xOC0uNC40MDQtLjQiIGZpbGw9IiNFNkU3RTgiLz48cGF0aCBkPSJNMTEuNTM4IDE2LjI4aDIwLjE0OGMuMjIyIDAgLjQwMy4xOC40MDMuNHY2LjUyN2MwIC4yMjItLjE4Mi40LS40MDQuNEgxMS41MzhjLS4yMjMgMC0uNDA0LS4xNzgtLjQwNC0uNFYxNi42OGMwLS4yMi4xOC0uNC40MDQtLjR6IiBzdHJva2U9IiM2Rjc4OTMiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0zMi4wOSAxNi4yOHY2LjkyN2MwIC4yMjItLjE4LjQtLjQwNC40aC0yMC4xNWMtLjIyIDAtLjQtLjE4LS40LS40di0xLjJoMTguMTZjLjY1MyAwIDEuMTgtLjUyNiAxLjE4LTEuMTc0VjE2LjI4aDEuNjEzeiIgc3Ryb2tlPSIjNkY3ODkzIiBmaWxsPSIjNkU3NzkyIiBvcGFjaXR5PSIuMTUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0zMC40NzcgMTYuMjhoLTMuMjI0di0xLjYwNGMwLS4yMjMuMTgzLS40MDQuNDA3LS40MDRoMi40MWMuMjI2IDAgLjQwNy4xOC40MDcuNDA0djEuNjAzek0xNS45NyAxNi4yOGgtMy4yMjR2LTEuNjA0YzAtLjIyMy4xODItLjQwNC40MDctLjQwNGgyLjQxYy4yMjYgMCAuNDA3LjE4LjQwNy40MDR2MS42MDN6TTI1LjY0IDE2LjI4SDIyLjQydi0xLjYwNGMwLS4yMjMuMTgyLS40MDQuNDA2LS40MDRoMi40MWMuMjI2IDAgLjQwNy4xOC40MDcuNDA0djEuNjAzek0yMC44MDYgMTYuMjhoLTMuMjI0di0xLjYwNGMwLS4yMjMuMTgyLS40MDQuNDA2LS40MDRIMjAuNGMuMjI0IDAgLjQwNi4xOC40MDYuNDA0djEuNjAzeiIgc3Ryb2tlPSIjNkY3ODkzIiBmaWxsPSIjRTZFN0U4IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMTguNTU3IDE5LjkxYzAgMS4wMjUtLjgzNyAxLjg1Ny0xLjg3IDEuODU3LTEuMDMgMC0xLjg2Ny0uODMyLTEuODY3LTEuODU4IDAtMS4wMjcuODM3LTEuODU4IDEuODY4LTEuODU4IDEuMDMyIDAgMS44Ny44MyAxLjg3IDEuODU3ek0yMy40OCAxOS45MWMwIDEuMDI1LS44MzYgMS44NTctMS44NjggMS44NTdzLTEuODctLjgzMi0xLjg3LTEuODU4YzAtMS4wMjcuODM4LTEuODU4IDEuODctMS44NThzMS44NjguODMgMS44NjggMS44NTd6TTI4LjQwNCAxOS45MWMwIDEuMDI1LS44MzcgMS44NTctMS44NjggMS44NTctMS4wMzIgMC0xLjg3LS44MzItMS44Ny0xLjg1OCAwLTEuMDI3LjgzOC0xLjg1OCAxLjg3LTEuODU4IDEuMDMgMCAxLjg2OC44MyAxLjg2OCAxLjg1N3oiIHN0cm9rZT0iIzZGNzg5MyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTE4LjU1NyAxOS45MjJjMCAxLjAyNi0uODM3IDEuODU4LTEuODcgMS44NTgtMS4wMyAwLTEuODY3LS44MzItMS44NjctMS44NTggMC0xLjAyNS44MzctMS44NTcgMS44NjgtMS44NTcgMS4wMzIgMCAxLjg3LjgzMiAxLjg3IDEuODU3TTIzLjQ4IDE5LjkyMmMwIDEuMDI2LS44MzYgMS44NTgtMS44NjggMS44NThzLTEuODctLjgzMi0xLjg3LTEuODU4YzAtMS4wMjUuODM4LTEuODU3IDEuODctMS44NTdzMS44NjguODMyIDEuODY4IDEuODU3TTI4LjQwNCAxOS45MjJjMCAxLjAyNi0uODM3IDEuODU4LTEuODY4IDEuODU4LTEuMDMyIDAtMS44Ny0uODMyLTEuODctMS44NTggMC0xLjAyNS44MzgtMS44NTcgMS44Ny0xLjg1NyAxLjAzIDAgMS44NjguODMyIDEuODY4IDEuODU3IiBmaWxsPSIjNkY3ODkzIiBvcGFjaXR5PSIuNSIvPjwvZz48L3N2Zz4='; /** From 59e0c75abf4ca5e7c590103be041f20086ecf0a8 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 6 Nov 2017 10:17:53 -0500 Subject: [PATCH 550/721] Fix shape of boolean tilt reporter --- src/blocks/scratch3_wedo2.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/scratch3_wedo2.js b/src/blocks/scratch3_wedo2.js index abc09e5d..b2d082ac 100644 --- a/src/blocks/scratch3_wedo2.js +++ b/src/blocks/scratch3_wedo2.js @@ -544,7 +544,7 @@ class Scratch3WeDo2Blocks { { opcode: 'isTilted', text: 'tilted [DIRECTION]?', - blockType: BlockType.REPORTER, + blockType: BlockType.BOOLEAN, arguments: { DIRECTION: { type: ArgumentType.STRING, From 73ffc6a7972bcc831ad4824c750f927da1c32eb9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 31 Oct 2017 11:12:26 -0400 Subject: [PATCH 551/721] Ask/answer opcode implementation with runtime events. --- src/blocks/scratch3_sensing.js | 58 +++++++++++++++++++++++++++++++++- test/unit/blocks_sensing.js | 34 ++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 test/unit/blocks_sensing.js diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index 559b0e9c..d9c2ab12 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -7,6 +7,21 @@ class Scratch3SensingBlocks { * @type {Runtime} */ this.runtime = runtime; + + /** + * The "answer" block value. + * @type {string} + */ + this._answer = ''; + + /** + * The list of queued questions and respective `resolve` callbacks. + * @type {!Array} + */ + this._questionList = []; + + this.runtime.on('ANSWER', this._onAnswer.bind(this)); + this.runtime.on('PROJECT_STOP_ALL', this._clearAllQuestions.bind(this)); } /** @@ -28,10 +43,51 @@ class Scratch3SensingBlocks { sensing_keypressed: this.getKeyPressed, sensing_current: this.current, sensing_dayssince2000: this.daysSince2000, - sensing_loudness: this.getLoudness + sensing_loudness: this.getLoudness, + sensing_askandwait: this.askAndWait, + sensing_answer: this.getAnswer }; } + _onAnswer (answer) { + this._answer = answer; + const questionObj = this._questionList.shift(); + if (questionObj) { + const resolve = questionObj[1]; + resolve(); + this._askNextQuestion(); + } + } + + _enqueueAsk (question, resolve) { + this._questionList.push([question, resolve]); + } + + _askNextQuestion () { + if (this._questionList.length > 0) { + this.runtime.emit('QUESTION', this._questionList[0][0]); + } + } + + _clearAllQuestions () { + this._questionList = []; + this.runtime.emit('QUESTION', null); + } + + askAndWait (args) { + return new Promise(resolve => { + const isQuestionAsked = this._questionList.length > 0; + this._enqueueAsk(args.QUESTION, resolve); + if (!isQuestionAsked) { + this._askNextQuestion(); + } + }); + } + + getAnswer () { + return this._answer; + } + touchingObject (args, util) { const requestedObject = args.TOUCHINGOBJECTMENU; if (requestedObject === '_mouse_') { diff --git a/test/unit/blocks_sensing.js b/test/unit/blocks_sensing.js new file mode 100644 index 00000000..e0c60b4b --- /dev/null +++ b/test/unit/blocks_sensing.js @@ -0,0 +1,34 @@ +const test = require('tap').test; +const Sensing = require('../../src/blocks/scratch3_sensing'); +const Runtime = require('../../src/engine/runtime'); + +test('getPrimitives', t => { + const rt = new Runtime(); + const s = new Sensing(rt); + t.type(s.getPrimitives(), 'object'); + t.end(); +}); + +test('ask and answer', t => { + const rt = new Runtime(); + const s = new Sensing(rt); + + const expectedQuestion = 'a question'; + const expectedAnswer = 'the answer'; + + // Test is written out of order because of promises, follow the (#) comments. + rt.addListener('QUESTION', question => { + // (2) Assert the question is correct, then emit the answer + t.strictEqual(question, expectedQuestion); + rt.emit('ANSWER', expectedAnswer); + }); + + // (1) Emit the question. + const promise = s.askAndWait({QUESTION: expectedQuestion}); + + // (3) Ask block resolves after the answer is emitted. + promise.then(() => { + t.strictEqual(s.getAnswer(), expectedAnswer); + t.end(); + }); +}); From cd9004ce5badc66b6e323cffcf091bc08da73706 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Fri, 3 Nov 2017 11:40:20 -0400 Subject: [PATCH 552/721] Write src/engine/execute so it can be optimized Testing with implicitly casted `value` and `value.then` is deoptimizing isPromise, which deoptimizes execute. In this case deoptimizing means that the JavaScript VM cannot compile the functions into a form that can be run faster. --- src/engine/execute.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/engine/execute.js b/src/engine/execute.js index a4bf76b2..24864592 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -8,7 +8,11 @@ const {Map} = require('immutable'); * @return {boolean} True if the value appears to be a Promise. */ const isPromise = function (value) { - return value && value.then && typeof value.then === 'function'; + return ( + value !== null && + typeof value === 'object' && + typeof value.then === 'function' + ); }; /** From fbeb1de1c1f0dd76b061054667c50dfa89b8c35f Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 6 Nov 2017 16:57:16 -0500 Subject: [PATCH 553/721] Add music extension file --- src/blocks/scratch3_music.js | 301 +++++++++++++++++++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 src/blocks/scratch3_music.js diff --git a/src/blocks/scratch3_music.js b/src/blocks/scratch3_music.js new file mode 100644 index 00000000..b7cde961 --- /dev/null +++ b/src/blocks/scratch3_music.js @@ -0,0 +1,301 @@ +const ArgumentType = require('../extension-support/argument-type'); +const BlockType = require('../extension-support/block-type'); +const Clone = require('../util/clone'); +const Cast = require('../util/cast'); +const MathUtil = require('../util/math-util'); + +const drumNames = [ + 'Snare Drum', + 'Bass Drum', + 'Side Stick', + 'Crash Cymbal', + 'Open Hi-Hat', + 'Closed Hi-Hat', + 'Tambourine', + 'Hand Clap', + 'Claves', + 'Wood Block', + 'Cowbell', + 'Triangle', + 'Bongo', + 'Conga', + 'Cabasa', + 'Guiro', + 'Vibraslap', + 'Open Cuica' +]; + +const instrumentNames = [ + 'Piano', + 'Electric Piano', + 'Organ', + 'Guitar', + 'Electric Guitar', + 'Bass', + 'Pizzicato', + 'Cello', + 'Trombone', + 'Clarinet', + 'Saxophone', + 'Flute', + 'Wooden Flute', + 'Bassoon', + 'Choir', + 'Vibraphone', + 'Music Box', + 'Steel Drum', + 'Marimba', + 'Synth Lead', + 'Synth Pad' +]; + +/** + * Class for the music-related blocks in Scratch 3.0 + * @param {Runtime} runtime - the runtime instantiating this block package. + * @constructor + */ +class Scratch3MusicBlocks { + constructor (runtime) { + /** + * The runtime instantiating this block package. + * @type {Runtime} + */ + this.runtime = runtime; + + this.drumMenu = this.buildMenu(drumNames); + this.instrumentMenu = this.buildMenu(instrumentNames); + } + + buildMenu (names) { + const menu = []; + for (let i = 0; i < names.length; i++) { + const entry = {}; + const num = i + 1; // Menu numbers are one-indexed + entry.text = `(${num}) ${names[i]}`; + entry.value = String(num); + menu.push(entry); + } + return menu; + } + + /** + * The key to load & store a target's music-related state. + * @type {string} + */ + static get STATE_KEY () { + return 'Scratch.music'; + } + + /** + * The default music-related state, to be used when a target has no existing music state. + * @type {MusicState} + */ + static get DEFAULT_MUSIC_STATE () { + return { + currentInstrument: 0 + }; + } + + /** + * The minimum and maximum MIDI note numbers, for clamping the input to play note. + * @type {{min: number, max: number}} + */ + static get MIDI_NOTE_RANGE () { + return {min: 36, max: 96}; // C2 to C7 + } + + /** + * The minimum and maximum beat values, for clamping the duration of play note, play drum and rest. + * 100 beats at the default tempo of 60bpm is 100 seconds. + * @type {{min: number, max: number}} + */ + static get BEAT_RANGE () { + return {min: 0, max: 100}; + } + + /** The minimum and maximum tempo values, in bpm. + * @type {{min: number, max: number}} + */ + static get TEMPO_RANGE () { + return {min: 20, max: 500}; + } + + /** + * @param {Target} target - collect music state for this target. + * @returns {MusicState} the mutable music state associated with that target. This will be created if necessary. + * @private + */ + _getMusicState (target) { + let musicState = target.getCustomState(Scratch3MusicBlocks.STATE_KEY); + if (!musicState) { + musicState = Clone.simple(Scratch3MusicBlocks.DEFAULT_MUSIC_STATE); + target.setCustomState(Scratch3MusicBlocks.STATE_KEY, musicState); + } + return musicState; + } + + /** + * @returns {object} metadata for this extension and its blocks. + */ + getInfo () { + return { + id: 'music', + name: 'Music', + blocks: [ + { + opcode: 'playDrumForBeats', + blockType: BlockType.COMMAND, + text: 'play drum [DRUM] for [BEATS] beats', + arguments: { + DRUM: { + type: ArgumentType.NUMBER, + menu: 'drums', + defaultValue: 1 + }, + BEATS: { + type: ArgumentType.NUMBER, + defaultValue: 0.25 + } + } + }, + { + opcode: 'restForBeats', + blockType: BlockType.COMMAND, + text: 'rest for [BEATS] beats', + arguments: { + BEATS: { + type: ArgumentType.NUMBER, + defaultValue: 0.25 + } + } + }, + { + opcode: 'playNoteForBeats', + blockType: BlockType.COMMAND, + text: 'play note [NOTE] for [BEATS] beats', + arguments: { + NOTE: { + type: ArgumentType.NUMBER, + defaultValue: 60 + }, + BEATS: { + type: ArgumentType.NUMBER, + defaultValue: 0.25 + } + } + }, + { + opcode: 'setInstrument', + blockType: BlockType.COMMAND, + text: 'set instrument to [INSTRUMENT]', + arguments: { + INSTRUMENT: { + type: ArgumentType.NUMBER, + menu: 'instruments', + defaultValue: 1 + } + } + }, + { + opcode: 'setTempo', + blockType: BlockType.COMMAND, + text: 'set tempo to [TEMPO]', + arguments: { + TEMPO: { + type: ArgumentType.NUMBER, + defaultValue: 60 + } + } + }, + { + opcode: 'changeTempo', + blockType: BlockType.COMMAND, + text: 'change tempo by [TEMPO]', + arguments: { + TEMPO: { + type: ArgumentType.NUMBER, + defaultValue: 20 + } + } + }, + { + opcode: 'getTempo', + text: 'tempo', + blockType: BlockType.REPORTER + } + ], + menus: { + drums: this.drumMenu, + instruments: this.instrumentMenu + } + }; + } + + playDrumForBeats (args, util) { + let drum = Cast.toNumber(args.DRUM); + drum -= 1; // drums are one-indexed + if (typeof this.runtime.audioEngine === 'undefined') return; + drum = MathUtil.wrapClamp(drum, 0, this.runtime.audioEngine.numDrums - 1); + let beats = Cast.toNumber(args.BEATS); + beats = this._clampBeats(beats); + if (util.target.audioPlayer === null) return; + return util.target.audioPlayer.playDrumForBeats(drum, beats); + } + + restForBeats (args) { + let beats = Cast.toNumber(args.BEATS); + beats = this._clampBeats(beats); + if (typeof this.runtime.audioEngine === 'undefined') return; + return this.runtime.audioEngine.waitForBeats(beats); + } + + playNoteForBeats (args, util) { + let note = Cast.toNumber(args.NOTE); + note = MathUtil.clamp(note, Scratch3MusicBlocks.MIDI_NOTE_RANGE.min, Scratch3MusicBlocks.MIDI_NOTE_RANGE.max); + let beats = Cast.toNumber(args.BEATS); + beats = this._clampBeats(beats); + const musicState = this._getMusicState(util.target); + const inst = musicState.currentInstrument; + if (typeof this.runtime.audioEngine === 'undefined') return; + return this.runtime.audioEngine.playNoteForBeatsWithInstAndVol(note, beats, inst, 100); + } + + setInstrument (args, util) { + const musicState = this._getMusicState(util.target); + let instNum = Cast.toNumber(args.INSTRUMENT); + instNum -= 1; // instruments are one-indexed + if (typeof this.runtime.audioEngine === 'undefined') return; + instNum = MathUtil.wrapClamp(instNum, 0, this.runtime.audioEngine.numInstruments - 1); + musicState.currentInstrument = instNum; + return this.runtime.audioEngine.instrumentPlayer.loadInstrument(musicState.currentInstrument); + } + + _clampBeats (beats) { + return MathUtil.clamp(beats, Scratch3MusicBlocks.BEAT_RANGE.min, Scratch3MusicBlocks.BEAT_RANGE.max); + } + + setTempo (args) { + const tempo = Cast.toNumber(args.TEMPO); + this._updateTempo(tempo); + } + + changeTempo (args) { + const change = Cast.toNumber(args.TEMPO); + if (typeof this.runtime.audioEngine === 'undefined') return; + const tempo = change + this.runtime.audioEngine.currentTempo; + this._updateTempo(tempo); + } + + _updateTempo (tempo) { + tempo = MathUtil.clamp(tempo, Scratch3MusicBlocks.TEMPO_RANGE.min, Scratch3MusicBlocks.TEMPO_RANGE.max); + if (typeof this.runtime.audioEngine === 'undefined') return; + this.runtime.audioEngine.setTempo(tempo); + } + + getTempo () { + if (typeof this.runtime.audioEngine === 'undefined') return; + return this.runtime.audioEngine.currentTempo; + } +} + +module.exports = Scratch3MusicBlocks; From 888da915a59583064488a03c8c7ad6cb92d0355a Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Mon, 6 Nov 2017 17:28:34 -0500 Subject: [PATCH 554/721] Replace duplicate documentation on getProcedureParamNames Replace the duplicate getProcedureDefintion documentation block above getProcedureParamNames. --- src/engine/blocks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index ba9077fc..92a2d58e 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -164,9 +164,9 @@ class Blocks { } /** - * Get the procedure definition for a given name. + * Get names of parameters for the given procedure. * @param {?string} name Name of procedure to query. - * @return {?string} ID of procedure definition. + * @return {?Array.} List of param names for a procedure. */ getProcedureParamNames (name) { for (const id in this._blocks) { From cb31f206a085d7cf1ad85537d3545a6c003e4b73 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 6 Nov 2017 17:41:57 -0500 Subject: [PATCH 555/721] Improved test coverage of deleteSprite() in src/virtual-machine. Fixed bug where null check was happening after looking up property of given target. --- src/virtual-machine.js | 5 +++- test/unit/virtual-machine.js | 44 ++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 6971e450..fe060104 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -474,9 +474,12 @@ class VirtualMachine extends EventEmitter { */ deleteSprite (targetId) { const target = this.runtime.getTargetById(targetId); - const targetIndexBeforeDelete = this.runtime.targets.map(t => t.id).indexOf(target.id); + // const targetIndexBeforeDelete = this.runtime.targets.map(t => t.id).indexOf(target.id); + // can't call target.id if target doesn't exist. + // moving below if (target) { + const targetIndexBeforeDelete = this.runtime.targets.map(t => t.id).indexOf(target.id); if (!target.isSprite()) { throw new Error('Cannot delete non-sprite targets.'); } diff --git a/test/unit/virtual-machine.js b/test/unit/virtual-machine.js index 624dddcd..2aa10ec8 100644 --- a/test/unit/virtual-machine.js +++ b/test/unit/virtual-machine.js @@ -110,6 +110,50 @@ test('renameSprite does not increment when renaming to the same name', t => { t.end(); }); +test('deleteSprite throws when used on a non-sprite target', t => { + const vm = new VirtualMachine(); + vm.runtime.targets = [{ + id: 'id', + isSprite: () => false + }]; + t.throws( + (() => vm.deleteSprite('id')), + new Error ('Cannot delete non-sprite targets.') + ); + t.end(); +}); + +test('deleteSprite throws when there is no sprite for the given target', t => { + const vm = new VirtualMachine(); + vm.runtime.targets = [{ + id: 'id', + isSprite: () => true, + sprite: null + }]; + t.throws( + (() => vm.deleteSprite('id')), + new Error ('No sprite associated with this target.') + ); + t.end(); +}); + +test('deleteSprite throws when there is no target with given id', t => { + const vm = new VirtualMachine(); + vm.runtime.targets = [{ + id: 'id', + isSprite: () => true, + sprite: { + name: 'this name' + } + }]; + vm.runtime.getTargetById = () => null; + t.throws( + (() => vm.deleteSprite('id')), + new Error ('No target with the provided id.') + ); + t.end(); +}); + test('emitWorkspaceUpdate', t => { const vm = new VirtualMachine(); vm.runtime.targets = [ From 78847de660b9cdbbddeb8b4dad7a6dbda2e0a0a2 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Fri, 3 Nov 2017 11:43:19 -0400 Subject: [PATCH 556/721] Extract handleReport and add arguments to prevent closure alloc `handleReport` inside `src/engine/execute.js`'s `execute` method needs to allocate a closure to be able to refer to the higher scoped variables. `execute` is called frequently that this has a noticable impact on memory allocation and later collection. Extract handleReport and add arguments to prevent the allocation. --- src/engine/execute.js | 111 ++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 52 deletions(-) diff --git a/src/engine/execute.js b/src/engine/execute.js index a4bf76b2..e0da40cc 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -11,6 +11,62 @@ const isPromise = function (value) { return value && value.then && typeof value.then === 'function'; }; +/** + * Handle any reported value from the primitive, either directly returned + * or after a promise resolves. + * @param {*} resolvedValue Value eventually returned from the primitive. + * @param {!Sequencer} sequencer Sequencer stepping the thread for the ran + * primitive. + * @param {!Thread} thread Thread containing the primitive. + * @param {!string} currentBlockId Id of the block in its thread for value from + * the primitive. + * @param {!boolean} isHat Is the current block a hat? + */ +// @todo move this to callback attached to the thread when we have performance +// metrics (dd) +const handleReport = function ( + resolvedValue, sequencer, thread, currentBlockId, isHat) { + thread.pushReportedValue(resolvedValue); + if (isHat) { + // Hat predicate was evaluated. + if (sequencer.runtime.getIsEdgeActivatedHat(opcode)) { + // If this is an edge-activated hat, only proceed if the value is + // true and used to be false, or the stack was activated explicitly + // via stack click + if (!thread.stackClick) { + const oldEdgeValue = sequencer.runtime.updateEdgeActivatedValue( + currentBlockId, + resolvedValue + ); + const edgeWasActivated = !oldEdgeValue && resolvedValue; + if (!edgeWasActivated) { + sequencer.retireThread(thread); + } + } + } else if (!resolvedValue) { + // Not an edge-activated hat: retire the thread + // if predicate was false. + sequencer.retireThread(thread); + } + } else { + // In a non-hat, report the value visually if necessary if + // at the top of the thread stack. + if (typeof resolvedValue !== 'undefined' && thread.atStackTop()) { + if (thread.stackClick) { + sequencer.runtime.visualReport(currentBlockId, resolvedValue); + } + if (thread.updateMonitor) { + sequencer.runtime.requestUpdateMonitor(Map({ + id: currentBlockId, + value: String(resolvedValue) + })); + } + } + // Finished any yields. + thread.status = Thread.STATUS_RUNNING; + } +}; + /** * Execute a block. * @param {!Sequencer} sequencer Which sequencer is executing. @@ -61,55 +117,6 @@ const execute = function (sequencer, thread) { return; } - /** - * Handle any reported value from the primitive, either directly returned - * or after a promise resolves. - * @param {*} resolvedValue Value eventually returned from the primitive. - */ - // @todo move this to callback attached to the thread when we have performance - // metrics (dd) - const handleReport = function (resolvedValue) { - thread.pushReportedValue(resolvedValue); - if (isHat) { - // Hat predicate was evaluated. - if (runtime.getIsEdgeActivatedHat(opcode)) { - // If this is an edge-activated hat, only proceed if - // the value is true and used to be false, or the stack was activated - // explicitly via stack click - if (!thread.stackClick) { - const oldEdgeValue = runtime.updateEdgeActivatedValue( - currentBlockId, - resolvedValue - ); - const edgeWasActivated = !oldEdgeValue && resolvedValue; - if (!edgeWasActivated) { - sequencer.retireThread(thread); - } - } - } else if (!resolvedValue) { - // Not an edge-activated hat: retire the thread - // if predicate was false. - sequencer.retireThread(thread); - } - } else { - // In a non-hat, report the value visually if necessary if - // at the top of the thread stack. - if (typeof resolvedValue !== 'undefined' && thread.atStackTop()) { - if (thread.stackClick) { - runtime.visualReport(currentBlockId, resolvedValue); - } - if (thread.updateMonitor) { - runtime.requestUpdateMonitor(Map({ - id: currentBlockId, - value: String(resolvedValue) - })); - } - } - // Finished any yields. - thread.status = Thread.STATUS_RUNNING; - } - }; - // Hats and single-field shadows are implemented slightly differently // from regular blocks. // For hats: if they have an associated block function, @@ -124,7 +131,7 @@ const execute = function (sequencer, thread) { const keys = Object.keys(fields); if (keys.length === 1 && Object.keys(inputs).length === 0) { // One field and no inputs - treat as arg. - handleReport(fields[keys[0]].value); + handleReport(fields[keys[0]].value, sequencer, thread, currentBlockId, isHat); } else { log.warn(`Could not get implementation for opcode: ${opcode}`); } @@ -244,7 +251,7 @@ const execute = function (sequencer, thread) { } // Promise handlers primitiveReportedValue.then(resolvedValue => { - handleReport(resolvedValue); + handleReport(resolvedValue, sequencer, thread, currentBlockId, isHat); if (typeof resolvedValue === 'undefined') { let stackFrame; let nextBlockId; @@ -277,7 +284,7 @@ const execute = function (sequencer, thread) { thread.popStack(); }); } else if (thread.status === Thread.STATUS_RUNNING) { - handleReport(primitiveReportedValue); + handleReport(primitiveReportedValue, sequencer, thread, currentBlockId, isHat); } }; From bbe79817034250a733f131062adac2562411e870 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Fri, 3 Nov 2017 11:46:57 -0400 Subject: [PATCH 557/721] Extract blockUtility from execute to reduce allocations blockUtility as an object literal inside execute creates 11 objects, one for the object, and 10 for the function closures. As a separate object and allocated once, setting its sequencer and thread members, block functions can share the same util object. Extract blockUtility to cut down on allocations. --- src/engine/block-utility.js | 153 ++++++++++++++++++++++++++++++++++++ src/engine/execute.js | 62 ++++----------- 2 files changed, 168 insertions(+), 47 deletions(-) create mode 100644 src/engine/block-utility.js diff --git a/src/engine/block-utility.js b/src/engine/block-utility.js new file mode 100644 index 00000000..9fee1f7b --- /dev/null +++ b/src/engine/block-utility.js @@ -0,0 +1,153 @@ +const Thread = require('./thread'); + +/** + * @fileoverview + * Interface provided to block primitive functions for interacting with the + * runtime, thread, target, and convenient methods. + */ + +class BlockUtility { + constructor (sequencer = null, thread = null) { + /** + * A sequencer block primitives use to branch or start procedures with + * @type {?Sequencer} + */ + this.sequencer = sequencer; + + /** + * The block primitives thread with the block's target, stackFrame and + * modifiable status. + * @type {?Thread} + */ + this.thread = thread; + } + + /** + * The target the primitive is working on. + * @type {Target} + */ + get target () { + return this.thread.target; + } + + /** + * The runtime the block primitive is running in. + * @type {Runtime} + */ + get runtime () { + return this.sequencer.runtime; + } + + /** + * The stack frame used by loop and other blocks to track internal state. + * @type {object} + */ + get stackFrame () { + return this.thread.peekStackFrame().executionContext; + } + + /** + * Set the thread to yield. + */ + yield () { + this.thread.status = Thread.STATUS_YIELD; + } + + /** + * Start a branch in the current block. + * @param {number} branchNum Which branch to step to (i.e., 1, 2). + * @param {boolean} isLoop Whether this block is a loop. + */ + startBranch (branchNum, isLoop) { + this.sequencer.stepToBranch(this.thread, branchNum, isLoop); + } + + /** + * Stop all threads. + */ + stopAll () { + this.sequencer.runtime.stopAll(); + } + + /** + * Stop threads other on this target other than the thread holding the + * executed block. + */ + stopOtherTargetThreads () { + this.sequencer.runtime.stopForTarget(this.thread.target, this.thread); + } + + /** + * Stop this thread. + */ + stopThisScript () { + this.thread.stopThisScript(); + } + + /** + * Start a specified procedure on this thread. + * @param {string} procedureCode Procedure code for procedure to start. + */ + startProcedure (procedureCode) { + this.sequencer.stepToProcedure(this.thread, procedureCode); + } + + /** + * Get names for parameters for the given procedure. + * @param {string} procedureCode Procedure code for procedure to query. + * @return {Array.} List of param names for a procedure. + */ + getProcedureParamNames (procedureCode) { + return this.thread.target.blocks.getProcedureParamNames(procedureCode); + } + + /** + * Store a procedure parameter value by its name. + * @param {string} paramName The procedure's parameter name. + * @param {*} paramValue The procedure's parameter value. + */ + pushParam (paramName, paramValue) { + this.thread.pushParam(paramName, paramValue); + } + + /** + * Retrieve the stored parameter value for a given parameter name. + * @param {string} paramName The procedure's parameter name. + * @return {*} The parameter's current stored value. + */ + getParam (paramName) { + return this.thread.getParam(paramName); + } + + /** + * Start all relevant hats. + * @param {!string} requestedHat Opcode of hats to start. + * @param {object=} optMatchFields Optionally, fields to match on the hat. + * @param {Target=} optTarget Optionally, a target to restrict to. + * @return {Array.} List of threads started by this function. + */ + startHats (requestedHat, optMatchFields, optTarget) { + return ( + this.sequencer.runtime.startHats(requestedHat, optMatchFields, optTarget) + ); + } + + /** + * Query a named IO device. + * @param {string} device The name of like the device, like keyboard. + * @param {string} func The name of the device's function to query. + * @param {Array.<*>} args Arguments to pass to the device's function. + * @return {*} The expected output for the device's function. + */ + ioQuery (device, func, args) { + // Find the I/O device and execute the query/function call. + if ( + this.sequencer.runtime.ioDevices[device] && + this.sequencer.runtime.ioDevices[device][func]) { + const devObject = this.sequencer.runtime.ioDevices[device]; + return devObject[func].apply(devObject, args); + } + } +} + +module.exports = BlockUtility; diff --git a/src/engine/execute.js b/src/engine/execute.js index e0da40cc..fda8054a 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -1,7 +1,14 @@ +const BlockUtility = require('./block-utility'); const log = require('../util/log'); const Thread = require('./thread'); const {Map} = require('immutable'); +/** + * Single BlockUtility instance reused by execute for every pritimive ran. + * @const + */ +const blockUtility = new BlockUtility(); + /** * Utility function to determine if a value is a Promise. * @param {*} value Value to check for a Promise. @@ -20,12 +27,13 @@ const isPromise = function (value) { * @param {!Thread} thread Thread containing the primitive. * @param {!string} currentBlockId Id of the block in its thread for value from * the primitive. + * @param {!string} opcode opcode used to identify a block function primitive. * @param {!boolean} isHat Is the current block a hat? */ // @todo move this to callback attached to the thread when we have performance // metrics (dd) const handleReport = function ( - resolvedValue, sequencer, thread, currentBlockId, isHat) { + resolvedValue, sequencer, thread, currentBlockId, opcode, isHat) { thread.pushReportedValue(resolvedValue); if (isHat) { // Hat predicate was evaluated. @@ -131,7 +139,7 @@ const execute = function (sequencer, thread) { const keys = Object.keys(fields); if (keys.length === 1 && Object.keys(inputs).length === 0) { // One field and no inputs - treat as arg. - handleReport(fields[keys[0]].value, sequencer, thread, currentBlockId, isHat); + handleReport(fields[keys[0]].value, sequencer, thread, currentBlockId, opcode, isHat); } else { log.warn(`Could not get implementation for opcode: ${opcode}`); } @@ -193,49 +201,9 @@ const execute = function (sequencer, thread) { currentStackFrame.reported = {}; let primitiveReportedValue = null; - primitiveReportedValue = blockFunction(argValues, { - stackFrame: currentStackFrame.executionContext, - target: target, - yield: function () { - thread.status = Thread.STATUS_YIELD; - }, - startBranch: function (branchNum, isLoop) { - sequencer.stepToBranch(thread, branchNum, isLoop); - }, - stopAll: function () { - runtime.stopAll(); - }, - stopOtherTargetThreads: function () { - runtime.stopForTarget(target, thread); - }, - stopThisScript: function () { - thread.stopThisScript(); - }, - startProcedure: function (procedureCode) { - sequencer.stepToProcedure(thread, procedureCode); - }, - getProcedureParamNames: function (procedureCode) { - return blockContainer.getProcedureParamNames(procedureCode); - }, - pushParam: function (paramName, paramValue) { - thread.pushParam(paramName, paramValue); - }, - getParam: function (paramName) { - return thread.getParam(paramName); - }, - startHats: function (requestedHat, optMatchFields, optTarget) { - return ( - runtime.startHats(requestedHat, optMatchFields, optTarget) - ); - }, - ioQuery: function (device, func, args) { - // Find the I/O device and execute the query/function call. - if (runtime.ioDevices[device] && runtime.ioDevices[device][func]) { - const devObject = runtime.ioDevices[device]; - return devObject[func].apply(devObject, args); - } - } - }); + blockUtility.sequencer = sequencer; + blockUtility.thread = thread; + primitiveReportedValue = blockFunction(argValues, blockUtility); if (typeof primitiveReportedValue === 'undefined') { // No value reported - potentially a command block. @@ -251,7 +219,7 @@ const execute = function (sequencer, thread) { } // Promise handlers primitiveReportedValue.then(resolvedValue => { - handleReport(resolvedValue, sequencer, thread, currentBlockId, isHat); + handleReport(resolvedValue, sequencer, thread, currentBlockId, opcode, isHat); if (typeof resolvedValue === 'undefined') { let stackFrame; let nextBlockId; @@ -284,7 +252,7 @@ const execute = function (sequencer, thread) { thread.popStack(); }); } else if (thread.status === Thread.STATUS_RUNNING) { - handleReport(primitiveReportedValue, sequencer, thread, currentBlockId, isHat); + handleReport(primitiveReportedValue, sequencer, thread, currentBlockId, opcode, isHat); } }; From e3dfe24d7f8f2fae994fca87e6ca06911f7d55bd Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Fri, 3 Nov 2017 11:50:16 -0400 Subject: [PATCH 558/721] Store references to target in say blocks As part of reusing the block utility object, asynchronous say block functions cannot use the utility in a promise or timeout asynchronous handle. Store the needed target reference so utility is not needed later. --- src/blocks/scratch3_looks.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index df1a0246..399c6efa 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -245,11 +245,12 @@ class Scratch3LooksBlocks { sayforsecs (args, util) { this.say(args, util); + const _target = util.target; return new Promise(resolve => { this._bubbleTimeout = setTimeout(() => { this._bubbleTimeout = null; // Clear say bubble and proceed. - this._updateBubble(util.target, 'say', ''); + this._updateBubble(_target, 'say', ''); resolve(); }, 1000 * args.SECS); }); @@ -261,11 +262,12 @@ class Scratch3LooksBlocks { thinkforsecs (args, util) { this.think(args, util); + const _target = util.target; return new Promise(resolve => { this._bubbleTimeout = setTimeout(() => { this._bubbleTimeout = null; // Clear say bubble and proceed. - this._updateBubble(util.target, 'think', ''); + this._updateBubble(_target, 'think', ''); resolve(); }, 1000 * args.SECS); }); From b2941f8c5a420321d7a0b926a65220089f8fc61c Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 7 Nov 2017 11:27:33 -0500 Subject: [PATCH 559/721] Load music as internal extension --- src/extension-support/extension-manager.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/extension-support/extension-manager.js b/src/extension-support/extension-manager.js index 5cfa2b4a..938e791d 100644 --- a/src/extension-support/extension-manager.js +++ b/src/extension-support/extension-manager.js @@ -8,9 +8,11 @@ const BlockType = require('./block-type'); // TODO: change extension spec so that library info, including extension ID, can be collected through static methods const Scratch3PenBlocks = require('../blocks/scratch3_pen'); const Scratch3WeDo2Blocks = require('../blocks/scratch3_wedo2'); +const Scratch3MusicBlocks = require('../blocks/scratch3_music'); const builtinExtensions = { pen: Scratch3PenBlocks, - wedo2: Scratch3WeDo2Blocks + wedo2: Scratch3WeDo2Blocks, + music: Scratch3MusicBlocks }; /** From 533ed60d98915770e20faee5191c87364e85f2a8 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 7 Nov 2017 11:27:50 -0500 Subject: [PATCH 560/721] Update sb2 specmap with music extension blocks --- src/serialization/sb2_specmap.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/serialization/sb2_specmap.js b/src/serialization/sb2_specmap.js index cf5be1c8..b511d59c 100644 --- a/src/serialization/sb2_specmap.js +++ b/src/serialization/sb2_specmap.js @@ -418,7 +418,7 @@ const specMap = { ] }, 'playDrum': { - opcode: 'sound_playdrumforbeats', + opcode: 'music.playDrumForBeats', argMap: [ { type: 'input', @@ -433,7 +433,7 @@ const specMap = { ] }, 'rest:elapsed:from:': { - opcode: 'sound_restforbeats', + opcode: 'music.restForBeats', argMap: [ { type: 'input', @@ -443,7 +443,7 @@ const specMap = { ] }, 'noteOn:duration:elapsed:from:': { - opcode: 'sound_playnoteforbeats', + opcode: 'music.playNoteForBeats', argMap: [ { type: 'input', @@ -458,7 +458,7 @@ const specMap = { ] }, 'instrument:': { - opcode: 'sound_setinstrumentto', + opcode: 'music.setInstrument', argMap: [ { type: 'input', @@ -493,7 +493,7 @@ const specMap = { ] }, 'changeTempoBy:': { - opcode: 'sound_changetempoby', + opcode: 'music.changeTempo', argMap: [ { type: 'input', @@ -503,7 +503,7 @@ const specMap = { ] }, 'setTempoTo:': { - opcode: 'sound_settempotobpm', + opcode: 'music.setTempo', argMap: [ { type: 'input', @@ -513,7 +513,7 @@ const specMap = { ] }, 'tempo': { - opcode: 'sound_tempo', + opcode: 'music.getTempo', argMap: [ ] }, From 87cf546b038bc32f6c82bd36df7bf41f1271e382 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 7 Nov 2017 11:28:04 -0500 Subject: [PATCH 561/721] Remove music opcodes from sound bocks --- src/blocks/scratch3_sound.js | 76 +----------------------------------- 1 file changed, 1 insertion(+), 75 deletions(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 1f774574..6f5fed97 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -91,10 +91,6 @@ class Scratch3SoundBlocks { sound_play: this.playSound, sound_playuntildone: this.playSoundAndWait, sound_stopallsounds: this.stopAllSounds, - sound_playnoteforbeats: this.playNoteForBeats, - sound_playdrumforbeats: this.playDrumForBeats, - sound_restforbeats: this.restForBeats, - sound_setinstrumentto: this.setInstrument, sound_seteffectto: this.setEffect, sound_changeeffectby: this.changeEffect, sound_cleareffects: this.clearEffects, @@ -103,10 +99,7 @@ class Scratch3SoundBlocks { sound_effects_menu: this.effectsMenu, sound_setvolumeto: this.setVolume, sound_changevolumeby: this.changeVolume, - sound_volume: this.getVolume, - sound_settempotobpm: this.setTempo, - sound_changetempoby: this.changeTempo, - sound_tempo: this.getTempo + sound_volume: this.getVolume }; } @@ -167,50 +160,6 @@ class Scratch3SoundBlocks { util.target.audioPlayer.stopAllSounds(); } - playNoteForBeats (args, util) { - let note = Cast.toNumber(args.NOTE); - note = MathUtil.clamp(note, Scratch3SoundBlocks.MIDI_NOTE_RANGE.min, Scratch3SoundBlocks.MIDI_NOTE_RANGE.max); - let beats = Cast.toNumber(args.BEATS); - beats = this._clampBeats(beats); - const soundState = this._getSoundState(util.target); - const inst = soundState.currentInstrument; - const vol = soundState.volume; - if (typeof this.runtime.audioEngine === 'undefined') return; - return this.runtime.audioEngine.playNoteForBeatsWithInstAndVol(note, beats, inst, vol); - } - - playDrumForBeats (args, util) { - let drum = Cast.toNumber(args.DRUM); - drum -= 1; // drums are one-indexed - if (typeof this.runtime.audioEngine === 'undefined') return; - drum = MathUtil.wrapClamp(drum, 0, this.runtime.audioEngine.numDrums - 1); - let beats = Cast.toNumber(args.BEATS); - beats = this._clampBeats(beats); - if (util.target.audioPlayer === null) return; - return util.target.audioPlayer.playDrumForBeats(drum, beats); - } - - restForBeats (args) { - let beats = Cast.toNumber(args.BEATS); - beats = this._clampBeats(beats); - if (typeof this.runtime.audioEngine === 'undefined') return; - return this.runtime.audioEngine.waitForBeats(beats); - } - - _clampBeats (beats) { - return MathUtil.clamp(beats, Scratch3SoundBlocks.BEAT_RANGE.min, Scratch3SoundBlocks.BEAT_RANGE.max); - } - - setInstrument (args, util) { - const soundState = this._getSoundState(util.target); - let instNum = Cast.toNumber(args.INSTRUMENT); - instNum -= 1; // instruments are one-indexed - if (typeof this.runtime.audioEngine === 'undefined') return; - instNum = MathUtil.wrapClamp(instNum, 0, this.runtime.audioEngine.numInstruments - 1); - soundState.currentInstrument = instNum; - return this.runtime.audioEngine.instrumentPlayer.loadInstrument(soundState.currentInstrument); - } - setEffect (args, util) { this._updateEffect(args, util, false); } @@ -273,29 +222,6 @@ class Scratch3SoundBlocks { return soundState.volume; } - setTempo (args) { - const tempo = Cast.toNumber(args.TEMPO); - this._updateTempo(tempo); - } - - changeTempo (args) { - const change = Cast.toNumber(args.TEMPO); - if (typeof this.runtime.audioEngine === 'undefined') return; - const tempo = change + this.runtime.audioEngine.currentTempo; - this._updateTempo(tempo); - } - - _updateTempo (tempo) { - tempo = MathUtil.clamp(tempo, Scratch3SoundBlocks.TEMPO_RANGE.min, Scratch3SoundBlocks.TEMPO_RANGE.max); - if (typeof this.runtime.audioEngine === 'undefined') return; - this.runtime.audioEngine.setTempo(tempo); - } - - getTempo () { - if (typeof this.runtime.audioEngine === 'undefined') return; - return this.runtime.audioEngine.currentTempo; - } - soundsMenu (args) { return args.SOUND_MENU; } From 9bd48e8761384e62223de7850034062cb58e7cb7 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 7 Nov 2017 15:21:20 -0500 Subject: [PATCH 562/721] Mark the buildMenu function private --- src/blocks/scratch3_music.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/blocks/scratch3_music.js b/src/blocks/scratch3_music.js index b7cde961..cdd9fc33 100644 --- a/src/blocks/scratch3_music.js +++ b/src/blocks/scratch3_music.js @@ -62,11 +62,12 @@ class Scratch3MusicBlocks { */ this.runtime = runtime; - this.drumMenu = this.buildMenu(drumNames); - this.instrumentMenu = this.buildMenu(instrumentNames); + this.drumMenu = this._buildMenu(drumNames); + this.instrumentMenu = this._buildMenu(instrumentNames); } buildMenu (names) { + _buildMenu (names) { const menu = []; for (let i = 0; i < names.length; i++) { const entry = {}; From 112bb55b9b02494fbf78cddea60d706ef3cff086 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 7 Nov 2017 15:21:47 -0500 Subject: [PATCH 563/721] JSDoc --- src/blocks/scratch3_music.js | 71 +++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/src/blocks/scratch3_music.js b/src/blocks/scratch3_music.js index cdd9fc33..f70fd48a 100644 --- a/src/blocks/scratch3_music.js +++ b/src/blocks/scratch3_music.js @@ -4,6 +4,10 @@ const Clone = require('../util/clone'); const Cast = require('../util/cast'); const MathUtil = require('../util/math-util'); +/** + * An array of drum names, used in the play drum block. + * @type {string[]} + */ const drumNames = [ 'Snare Drum', 'Bass Drum', @@ -25,6 +29,10 @@ const drumNames = [ 'Open Cuica' ]; +/** + * An array of instrument names, used in the set instrument block. + * @type {string[]} + */ const instrumentNames = [ 'Piano', 'Electric Piano', @@ -66,7 +74,13 @@ class Scratch3MusicBlocks { this.instrumentMenu = this._buildMenu(instrumentNames); } - buildMenu (names) { + /** + * Build a menu using an array of strings. + * Used for creating the drum and instrument menus. + * @param {string[]} names - An array of names. + * @return {array} - An array of objects with text and value properties, for constructing a block menu. + * @private + */ _buildMenu (names) { const menu = []; for (let i = 0; i < names.length; i++) { @@ -232,6 +246,14 @@ class Scratch3MusicBlocks { }; } + /** + * Play a drum sound for some number of beats. + * @param {object} args - the block arguments. + * @param {object} util - utility object provided by the runtime. + * @property {int} DRUM - the number of the drum to play. + * @property {number} BEATS - the duration in beats of the drum sound. + * @return {Promise} - a promise which will resolve at the end of the duration. + */ playDrumForBeats (args, util) { let drum = Cast.toNumber(args.DRUM); drum -= 1; // drums are one-indexed @@ -243,6 +265,13 @@ class Scratch3MusicBlocks { return util.target.audioPlayer.playDrumForBeats(drum, beats); } + /** + * Rest for some number of beats. + * @param {object} args - the block arguments. + * @param {object} util - utility object provided by the runtime. + * @property {number} BEATS - the duration in beats of the rest. + * @return {Promise} - a promise which will resolve at the end of the duration. + */ restForBeats (args) { let beats = Cast.toNumber(args.BEATS); beats = this._clampBeats(beats); @@ -250,6 +279,14 @@ class Scratch3MusicBlocks { return this.runtime.audioEngine.waitForBeats(beats); } + /** + * Play a note using the current musical instrument for some number of beats. + * @param {object} args - the block arguments. + * @param {object} util - utility object provided by the runtime. + * @property {number} NOTE - the pitch of the note to play, interpreted as a MIDI note number. + * @property {number} BEATS - the duration in beats of the note. + * @return {Promise} - a promise which will resolve at the end of the duration. + */ playNoteForBeats (args, util) { let note = Cast.toNumber(args.NOTE); note = MathUtil.clamp(note, Scratch3MusicBlocks.MIDI_NOTE_RANGE.min, Scratch3MusicBlocks.MIDI_NOTE_RANGE.max); @@ -261,6 +298,13 @@ class Scratch3MusicBlocks { return this.runtime.audioEngine.playNoteForBeatsWithInstAndVol(note, beats, inst, 100); } + /** + * Select an instrument for playing notes. + * @param {object} args - the block arguments. + * @param {object} util - utility object provided by the runtime. + * @property {int} INSTRUMENT - the number of the instrument to select. + * @return {Promise} - a promise which will resolve once the instrument has loaded. + */ setInstrument (args, util) { const musicState = this._getMusicState(util.target); let instNum = Cast.toNumber(args.INSTRUMENT); @@ -271,15 +315,31 @@ class Scratch3MusicBlocks { return this.runtime.audioEngine.instrumentPlayer.loadInstrument(musicState.currentInstrument); } + /** + * Clamp a duration in beats to the allowed min and max duration. + * @param {number} beats - a duration in beats. + * @return {number} - the clamped duration. + * @private + */ _clampBeats (beats) { return MathUtil.clamp(beats, Scratch3MusicBlocks.BEAT_RANGE.min, Scratch3MusicBlocks.BEAT_RANGE.max); } + /** + * Set the current tempo to a new value. + * @param {object} args - the block arguments. + * @property {number} TEMPO - the tempo, in beats per minute. + */ setTempo (args) { const tempo = Cast.toNumber(args.TEMPO); this._updateTempo(tempo); } + /** + * Change the current tempo by some amount. + * @param {object} args - the block arguments. + * @property {number} TEMPO - the amount to change the tempo, in beats per minute. + */ changeTempo (args) { const change = Cast.toNumber(args.TEMPO); if (typeof this.runtime.audioEngine === 'undefined') return; @@ -287,12 +347,21 @@ class Scratch3MusicBlocks { this._updateTempo(tempo); } + /** + * Update the current tempo, clamping it to the min and max allowable range. + * @param {number} tempo - the tempo to set, in beats per minute. + * @private + */ _updateTempo (tempo) { tempo = MathUtil.clamp(tempo, Scratch3MusicBlocks.TEMPO_RANGE.min, Scratch3MusicBlocks.TEMPO_RANGE.max); if (typeof this.runtime.audioEngine === 'undefined') return; this.runtime.audioEngine.setTempo(tempo); } + /** + * Get the current tempo. + * @return {number} - the current tempo, in beats per minute. + */ getTempo () { if (typeof this.runtime.audioEngine === 'undefined') return; return this.runtime.audioEngine.currentTempo; From dc4767646fce8cda2f22c33aed4464f45c3589f8 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 7 Nov 2017 16:26:20 -0500 Subject: [PATCH 564/721] Move music-related unit tests into a separate file --- test/unit/blocks_music.js | 48 ++++++++++++++++++++++++++++++++++++++ test/unit/blocks_sounds.js | 44 +++------------------------------- 2 files changed, 51 insertions(+), 41 deletions(-) create mode 100644 test/unit/blocks_music.js diff --git a/test/unit/blocks_music.js b/test/unit/blocks_music.js new file mode 100644 index 00000000..3b8f8d94 --- /dev/null +++ b/test/unit/blocks_music.js @@ -0,0 +1,48 @@ +const test = require('tap').test; +const Music = require('../../src/blocks/scratch3_music'); +let playedDrum; +let playedInstrument; +const runtime = { + audioEngine: { + numDrums: 3, + numInstruments: 3, + instrumentPlayer: { + loadInstrument: instrument => (playedInstrument = instrument) + } + } +}; +const blocks = new Music(runtime); +const util = { + target: { + audioPlayer: { + playDrumForBeats: drum => (playedDrum = drum) + } + } +}; + +test('playDrum uses 1-indexing and wrap clamps', t => { + let args = {DRUM: 1}; + blocks.playDrumForBeats(args, util); + t.strictEqual(playedDrum, 0); + + args = {DRUM: runtime.audioEngine.numDrums + 1}; + blocks.playDrumForBeats(args, util); + t.strictEqual(playedDrum, 0); + + t.end(); +}); + +test('setInstrument uses 1-indexing and wrap clamps', t => { + // Stub getMusicState + blocks._getMusicState = () => ({}); + + let args = {INSTRUMENT: 1}; + blocks.setInstrument(args, util); + t.strictEqual(playedInstrument, 0); + + args = {INSTRUMENT: runtime.audioEngine.numInstruments + 1}; + blocks.setInstrument(args, util); + t.strictEqual(playedInstrument, 0); + + t.end(); +}); diff --git a/test/unit/blocks_sounds.js b/test/unit/blocks_sounds.js index 753f46f0..9a44e7f9 100644 --- a/test/unit/blocks_sounds.js +++ b/test/unit/blocks_sounds.js @@ -1,18 +1,8 @@ const test = require('tap').test; const Sound = require('../../src/blocks/scratch3_sound'); let playedSound; -let playedDrum; -let playedInstrument; -const runtime = { - audioEngine: { - numDrums: 3, - numInstruments: 3, - instrumentPlayer: { - loadInstrument: instrument => (playedInstrument = instrument) - } - } -}; -const blocks = new Sound(runtime); + +const blocks = new Sound(); const util = { target: { sprite: { @@ -24,8 +14,7 @@ const util = { ] }, audioPlayer: { - playSound: soundId => (playedSound = soundId), - playDrumForBeats: drum => (playedDrum = drum) + playSound: soundId => (playedSound = soundId) } } }; @@ -82,30 +71,3 @@ test('playSound prioritizes sound name if given a string', t => { t.strictEqual(playedSound, 'fourth soundId'); t.end(); }); - -test('playDrum uses 1-indexing and wrap clamps', t => { - let args = {DRUM: 1}; - blocks.playDrumForBeats(args, util); - t.strictEqual(playedDrum, 0); - - args = {DRUM: runtime.audioEngine.numDrums + 1}; - blocks.playDrumForBeats(args, util); - t.strictEqual(playedDrum, 0); - - t.end(); -}); - -test('setInstrument uses 1-indexing and wrap clamps', t => { - // Stub getSoundState - blocks._getSoundState = () => ({}); - - let args = {INSTRUMENT: 1}; - blocks.setInstrument(args, util); - t.strictEqual(playedInstrument, 0); - - args = {INSTRUMENT: runtime.audioEngine.numInstruments + 1}; - blocks.setInstrument(args, util); - t.strictEqual(playedInstrument, 0); - - t.end(); -}); From c0fdbc2f4d2f3ddc78c1b39f0476624ac1cf32b2 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 7 Nov 2017 16:26:35 -0500 Subject: [PATCH 565/721] Fix sound integration test --- test/integration/sound.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/integration/sound.js b/test/integration/sound.js index 50100d70..1c5f20dc 100644 --- a/test/integration/sound.js +++ b/test/integration/sound.js @@ -1,12 +1,17 @@ +const Worker = require('tiny-worker'); const path = require('path'); const test = require('tap').test; const makeTestStorage = require('../fixtures/make-test-storage'); const extract = require('../fixtures/extract'); const VirtualMachine = require('../../src/index'); +const dispatch = require('../../src/dispatch/central-dispatch'); const uri = path.resolve(__dirname, '../fixtures/sound.sb2'); const project = extract(uri); +// By default Central Dispatch works with the Worker class built into the browser. Tell it to use TinyWorker instead. +dispatch.workerClass = Worker; + test('sound', t => { const vm = new VirtualMachine(); vm.attachStorage(makeTestStorage()); From 16ca994818f74892dd636d6cbb0e0e3ed97886ca Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 8 Nov 2017 10:37:38 -0500 Subject: [PATCH 566/721] Allow ask to use say bubble via events --- src/blocks/scratch3_looks.js | 4 ++++ src/blocks/scratch3_sensing.js | 25 +++++++++++++++++------ test/unit/blocks_sensing.js | 37 ++++++++++++++++++++++++++++++++-- 3 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index 399c6efa..70238a78 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -23,10 +23,14 @@ class Scratch3LooksBlocks { this._onTargetMoved = this._onTargetMoved.bind(this); this._onResetBubbles = this._onResetBubbles.bind(this); this._onTargetWillExit = this._onTargetWillExit.bind(this); + this._updateBubble = this._updateBubble.bind(this); // Reset all bubbles on start/stop this.runtime.on('PROJECT_STOP_ALL', this._onResetBubbles); this.runtime.on('targetWasRemoved', this._onTargetWillExit); + + // Enable other blocks to use bubbles like ask/answer + this.runtime.on('SAY', this._updateBubble); } /** diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index d9c2ab12..17dc87e9 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -53,19 +53,31 @@ class Scratch3SensingBlocks { this._answer = answer; const questionObj = this._questionList.shift(); if (questionObj) { - const resolve = questionObj[1]; + const [_question, resolve, target, wasVisible] = questionObj; + // If the target was visible when asked, hide the say bubble. + if (wasVisible) { + this.runtime.emit('SAY', target, 'say', ''); + } resolve(); this._askNextQuestion(); } } - _enqueueAsk (question, resolve) { - this._questionList.push([question, resolve]); + _enqueueAsk (question, resolve, target, wasVisible) { + this._questionList.push([question, resolve, target, wasVisible]); } _askNextQuestion () { if (this._questionList.length > 0) { - this.runtime.emit('QUESTION', this._questionList[0][0]); + const [question, _resolve, target, wasVisible] = this._questionList[0]; + // If the target is visible, emit a blank question and use the + // say event to trigger a bubble. + if (wasVisible) { + this.runtime.emit('SAY', target, 'say', question); + this.runtime.emit('QUESTION', ''); + } else { + this.runtime.emit('QUESTION', question); + } } } @@ -74,10 +86,11 @@ class Scratch3SensingBlocks { this.runtime.emit('QUESTION', null); } - askAndWait (args) { + askAndWait (args, util) { + const _target = util.target; return new Promise(resolve => { const isQuestionAsked = this._questionList.length > 0; - this._enqueueAsk(args.QUESTION, resolve); + this._enqueueAsk(args.QUESTION, resolve, _target, _target.visible); if (!isQuestionAsked) { this._askNextQuestion(); } diff --git a/test/unit/blocks_sensing.js b/test/unit/blocks_sensing.js index e0c60b4b..5c2b927a 100644 --- a/test/unit/blocks_sensing.js +++ b/test/unit/blocks_sensing.js @@ -9,9 +9,10 @@ test('getPrimitives', t => { t.end(); }); -test('ask and answer', t => { +test('ask and answer with a hidden target', t => { const rt = new Runtime(); const s = new Sensing(rt); + const util = {target: {visible: false}}; const expectedQuestion = 'a question'; const expectedAnswer = 'the answer'; @@ -24,7 +25,7 @@ test('ask and answer', t => { }); // (1) Emit the question. - const promise = s.askAndWait({QUESTION: expectedQuestion}); + const promise = s.askAndWait({QUESTION: expectedQuestion}, util); // (3) Ask block resolves after the answer is emitted. promise.then(() => { @@ -32,3 +33,35 @@ test('ask and answer', t => { t.end(); }); }); + +test('ask and answer with a visible target', t => { + const rt = new Runtime(); + const s = new Sensing(rt); + const util = {target: {visible: true}}; + + const expectedQuestion = 'a question'; + const expectedAnswer = 'the answer'; + + rt.removeAllListeners('SAY'); // Prevent say blocks from executing + + rt.addListener('SAY', (target, type, question) => { + // Should emit SAY with the question + t.strictEqual(question, expectedQuestion); + }); + + rt.addListener('QUESTION', question => { + // Question should be blank for a visible target + t.strictEqual(question, ''); + + // Remove the say listener and add a new one to assert bubble is cleared + // by setting say to empty string after answer is received. + rt.removeAllListeners('SAY'); + rt.addListener('SAY', (target, type, text) => { + t.strictEqual(text, ''); + t.end(); + }); + rt.emit('ANSWER', expectedAnswer); + }); + + s.askAndWait({QUESTION: expectedQuestion}, util); +}); From bf6b78137a819867b89843f047fbcea7d7a9ee48 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 8 Nov 2017 15:48:24 -0500 Subject: [PATCH 567/721] Test coverage for virtual-machine's deleteSprite and duplicateSprite functions. --- test/unit/virtual-machine.js | 130 ++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 2 deletions(-) diff --git a/test/unit/virtual-machine.js b/test/unit/virtual-machine.js index 2aa10ec8..199640d7 100644 --- a/test/unit/virtual-machine.js +++ b/test/unit/virtual-machine.js @@ -1,5 +1,8 @@ const test = require('tap').test; const VirtualMachine = require('../../src/virtual-machine.js'); +const Target = require('../../src/engine/target.js'); +const Sprite = require('../../src/sprites/sprite.js'); +const RenderedTarget = require('../../src/sprites/rendered-target'); test('renameSprite throws when there is no sprite with that id', t => { const vm = new VirtualMachine(); @@ -146,14 +149,137 @@ test('deleteSprite throws when there is no target with given id', t => { name: 'this name' } }]; - vm.runtime.getTargetById = () => null; t.throws( - (() => vm.deleteSprite('id')), + (() => vm.deleteSprite('id1')), new Error ('No target with the provided id.') ); t.end(); }); +test('deleteSprite deletes a sprite when given id is associated with a known sprite', t => { + const vm = new VirtualMachine(); + const spr = new Sprite(null, vm.runtime); + const currTarget = spr.createClone(); + + vm.runtime.targets = [currTarget]; + + t.equal(currTarget.sprite.clones.length, 1); + vm.deleteSprite(currTarget.id); + t.equal(currTarget.sprite.clones.length, 0); + t.end(); +}); + +test('deleteSprite sets editing target as null when sprite being deleted is current editing target, and the only target in the runtime', t => { + const vm = new VirtualMachine(); + const spr = new Sprite(null, vm.runtime); + const currTarget = spr.createClone(); + + vm.editingTarget = currTarget; + vm.runtime.targets = [currTarget]; + + vm.deleteSprite(currTarget.id); + + t.equal(vm.runtime.targets.length, 0); + t.equal(vm.editingTarget, null); + t.end(); +}); + +test('deleteSprite updates editingTarget when sprite being deleted is current editing target, and there is another target in the runtime', t => { + const vm = new VirtualMachine(); + const spr1 = new Sprite(null, vm.runtime); + const spr2 = new Sprite(null, vm.runtime); + const currTarget = spr1.createClone(); + const otherTarget = spr2.createClone(); + + vm.emitWorkspaceUpdate = () => null; + + vm.runtime.targets = [currTarget, otherTarget]; + vm.editingTarget = currTarget; + + t.equal(vm.runtime.targets.length, 2); + vm.deleteSprite(currTarget.id); + t.equal(vm.runtime.targets.length, 1); + t.equal(vm.editingTarget.id, otherTarget.id); + + // now let's try them in the other order in the runtime.targets list + + // can't reuse deleted targets + const currTarget2 = spr1.createClone(); + const otherTarget2 = spr2.createClone(); + + vm.runtime.targets = [otherTarget2, currTarget2]; + vm.editingTarget = currTarget2; + + t.equal(vm.runtime.targets.length, 2); + vm.deleteSprite(currTarget2.id); + t.equal(vm.editingTarget.id, otherTarget2.id); + t.equal(vm.runtime.targets.length, 1); + + t.end(); +}); + + + +test('duplicateSprite throws when there is no target with given id', t => { + const vm = new VirtualMachine(); + vm.runtime.targets = [{ + id: 'id', + isSprite: () => true, + sprite: { + name: 'this name' + } + }]; + t.throws( + (() => vm.duplicateSprite('id1')), + new Error('No target with the provided id') + ); + t.end(); +}); + +test('duplicateSprite throws when used on a non-sprite target', t => { + const vm = new VirtualMachine(); + vm.runtime.targets = [{ + id: 'id', + isSprite: () => false + }]; + t.throws( + (() => vm.duplicateSprite('id')), + new Error('Cannot duplicate non-sprite targets.') + ); + t.end(); +}); + +test('duplicateSprite throws when there is no sprite for the given target', t => { + const vm = new VirtualMachine(); + vm.runtime.targets = [{ + id: 'id', + isSprite: () => true, + sprite: null + }]; + t.throws( + (() => vm.duplicateSprite('id')), + new Error ('No sprite associated with this target.') + ); + t.end(); +}); + +test('duplicateSprite duplicates a sprite when given id is associated with known sprite', t => { + const vm = new VirtualMachine(); + const spr = new Sprite(null, vm.runtime); + const currTarget = spr.createClone(); + vm.editingTarget = currTarget; + + vm.emitWorkspaceUpdate = () => null; + + vm.runtime.targets = [currTarget]; + t.equal(vm.runtime.targets.length, 1); + vm.duplicateSprite(currTarget.id).then(() => { + t.equal(vm.runtime.targets.length, 2); + t.end(); + }); + +}); + test('emitWorkspaceUpdate', t => { const vm = new VirtualMachine(); vm.runtime.targets = [ From 70081132cb4adf7a4effd40b17ae2ee2e4f480a8 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 8 Nov 2017 15:49:51 -0500 Subject: [PATCH 568/721] Changes to src/virtual-machine based on minor bugs found during testing. This was supposed to be included with the previous commit. --- src/virtual-machine.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index fe060104..d45cca8d 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -495,7 +495,11 @@ class VirtualMachine extends EventEmitter { // Ensure editing target is switched if we are deleting it. if (clone === currentEditingTarget) { const nextTargetIndex = Math.min(this.runtime.targets.length - 1, targetIndexBeforeDelete); - this.setEditingTarget(this.runtime.targets[nextTargetIndex].id); + if (this.runtime.targets.length > 0){ + this.setEditingTarget(this.runtime.targets[nextTargetIndex].id); + } else { + this.editingTarget = null; + } } } // Sprite object should be deleted by GC. @@ -518,7 +522,7 @@ class VirtualMachine extends EventEmitter { } else if (!target.sprite) { throw new Error('No sprite associated with this target.'); } - target.duplicate().then(newTarget => { + return target.duplicate().then(newTarget => { this.runtime.targets.push(newTarget); this.setEditingTarget(newTarget.id); }); From 7fae93f6e6af93a0f690625788c49fabbb4f1bac Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 8 Nov 2017 15:59:35 -0500 Subject: [PATCH 569/721] Removing unnecessary comments from edits. --- src/virtual-machine.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index d45cca8d..5056e4ae 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -474,9 +474,6 @@ class VirtualMachine extends EventEmitter { */ deleteSprite (targetId) { const target = this.runtime.getTargetById(targetId); - // const targetIndexBeforeDelete = this.runtime.targets.map(t => t.id).indexOf(target.id); - // can't call target.id if target doesn't exist. - // moving below if (target) { const targetIndexBeforeDelete = this.runtime.targets.map(t => t.id).indexOf(target.id); From c31fff8c4cc9f63e06e3fce5f939eaeba629193e Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 9 Nov 2017 09:18:44 -0500 Subject: [PATCH 570/721] Fixing linter errors. --- src/virtual-machine.js | 2 ++ test/unit/virtual-machine.js | 16 +++++++--------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 5056e4ae..6dfc7aca 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -509,6 +509,8 @@ class VirtualMachine extends EventEmitter { /** * Duplicate a sprite. * @param {string} targetId ID of a target whose sprite to duplicate. + * @returns {Promise} Promise that resolves when duplicated target has + * been added to the runtime. */ duplicateSprite (targetId) { const target = this.runtime.getTargetById(targetId); diff --git a/test/unit/virtual-machine.js b/test/unit/virtual-machine.js index 199640d7..f0887039 100644 --- a/test/unit/virtual-machine.js +++ b/test/unit/virtual-machine.js @@ -1,8 +1,6 @@ const test = require('tap').test; const VirtualMachine = require('../../src/virtual-machine.js'); -const Target = require('../../src/engine/target.js'); const Sprite = require('../../src/sprites/sprite.js'); -const RenderedTarget = require('../../src/sprites/rendered-target'); test('renameSprite throws when there is no sprite with that id', t => { const vm = new VirtualMachine(); @@ -121,7 +119,7 @@ test('deleteSprite throws when used on a non-sprite target', t => { }]; t.throws( (() => vm.deleteSprite('id')), - new Error ('Cannot delete non-sprite targets.') + new Error('Cannot delete non-sprite targets.') ); t.end(); }); @@ -135,7 +133,7 @@ test('deleteSprite throws when there is no sprite for the given target', t => { }]; t.throws( (() => vm.deleteSprite('id')), - new Error ('No sprite associated with this target.') + new Error('No sprite associated with this target.') ); t.end(); }); @@ -151,7 +149,7 @@ test('deleteSprite throws when there is no target with given id', t => { }]; t.throws( (() => vm.deleteSprite('id1')), - new Error ('No target with the provided id.') + new Error('No target with the provided id.') ); t.end(); }); @@ -169,7 +167,8 @@ test('deleteSprite deletes a sprite when given id is associated with a known spr t.end(); }); -test('deleteSprite sets editing target as null when sprite being deleted is current editing target, and the only target in the runtime', t => { +// eslint-disable-next-line max-len +test('deleteSprite sets editing target as null when given sprite is current editing target, and the only target in the runtime', t => { const vm = new VirtualMachine(); const spr = new Sprite(null, vm.runtime); const currTarget = spr.createClone(); @@ -184,6 +183,7 @@ test('deleteSprite sets editing target as null when sprite being deleted is curr t.end(); }); +// eslint-disable-next-line max-len test('deleteSprite updates editingTarget when sprite being deleted is current editing target, and there is another target in the runtime', t => { const vm = new VirtualMachine(); const spr1 = new Sprite(null, vm.runtime); @@ -218,8 +218,6 @@ test('deleteSprite updates editingTarget when sprite being deleted is current ed t.end(); }); - - test('duplicateSprite throws when there is no target with given id', t => { const vm = new VirtualMachine(); vm.runtime.targets = [{ @@ -258,7 +256,7 @@ test('duplicateSprite throws when there is no sprite for the given target', t => }]; t.throws( (() => vm.duplicateSprite('id')), - new Error ('No sprite associated with this target.') + new Error('No sprite associated with this target.') ); t.end(); }); From 70959cc7f52f45ef3f3281aa748d22e463bae475 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 9 Nov 2017 17:19:34 -0500 Subject: [PATCH 571/721] First cut at turning lists into typed variables --- src/blocks/scratch3_data.js | 36 ++++++++++++++++---------------- src/engine/blocks.js | 4 ++-- src/engine/execute.js | 2 +- src/engine/list.js | 18 ---------------- src/engine/target.js | 31 ++++++++++----------------- src/engine/variable.js | 16 ++++++++++---- src/serialization/sb2.js | 12 +++++++---- src/serialization/sb2_specmap.js | 2 +- src/serialization/sb3.js | 14 ++----------- 9 files changed, 55 insertions(+), 80 deletions(-) delete mode 100644 src/engine/list.js diff --git a/src/blocks/scratch3_data.js b/src/blocks/scratch3_data.js index 551f717f..8ec4f124 100644 --- a/src/blocks/scratch3_data.js +++ b/src/blocks/scratch3_data.js @@ -52,8 +52,8 @@ class Scratch3DataBlocks { // If it is, report contents joined together with no separator. // If it's not, report contents joined together with a space. let allSingleLetters = true; - for (let i = 0; i < list.contents.length; i++) { - const listItem = list.contents[i]; + for (let i = 0; i < list.value.length; i++) { + const listItem = list.value[i]; if (!((typeof listItem === 'string') && (listItem.length === 1))) { allSingleLetters = false; @@ -61,73 +61,73 @@ class Scratch3DataBlocks { } } if (allSingleLetters) { - return list.contents.join(''); + return list.value.join(''); } - return list.contents.join(' '); + return list.value.join(' '); } addToList (args, util) { const list = util.target.lookupOrCreateList(args.LIST); - list.contents.push(args.ITEM); + list.value.push(args.ITEM); } deleteOfList (args, util) { const list = util.target.lookupOrCreateList(args.LIST); - const index = Cast.toListIndex(args.INDEX, list.contents.length); + const index = Cast.toListIndex(args.INDEX, list.value.length); if (index === Cast.LIST_INVALID) { return; } else if (index === Cast.LIST_ALL) { - list.contents = []; + list.value = []; return; } - list.contents.splice(index - 1, 1); + list.value.splice(index - 1, 1); } insertAtList (args, util) { const item = args.ITEM; const list = util.target.lookupOrCreateList(args.LIST); - const index = Cast.toListIndex(args.INDEX, list.contents.length + 1); + const index = Cast.toListIndex(args.INDEX, list.value.length + 1); if (index === Cast.LIST_INVALID) { return; } - list.contents.splice(index - 1, 0, item); + list.value.splice(index - 1, 0, item); } replaceItemOfList (args, util) { const item = args.ITEM; const list = util.target.lookupOrCreateList(args.LIST); - const index = Cast.toListIndex(args.INDEX, list.contents.length); + const index = Cast.toListIndex(args.INDEX, list.value.length); if (index === Cast.LIST_INVALID) { return; } - list.contents.splice(index - 1, 1, item); + list.value.splice(index - 1, 1, item); } getItemOfList (args, util) { const list = util.target.lookupOrCreateList(args.LIST); - const index = Cast.toListIndex(args.INDEX, list.contents.length); + const index = Cast.toListIndex(args.INDEX, list.value.length); if (index === Cast.LIST_INVALID) { return ''; } - return list.contents[index - 1]; + return list.value[index - 1]; } lengthOfList (args, util) { const list = util.target.lookupOrCreateList(args.LIST); - return list.contents.length; + return list.value.length; } listContainsItem (args, util) { const item = args.ITEM; const list = util.target.lookupOrCreateList(args.LIST); - if (list.contents.indexOf(item) >= 0) { + if (list.value.indexOf(item) >= 0) { return true; } // Try using Scratch comparison operator on each item. // (Scratch considers the string '123' equal to the number 123). - for (let i = 0; i < list.contents.length; i++) { - if (Cast.compare(list.contents[i], item) === 0) { + for (let i = 0; i < list.value.length; i++) { + if (Cast.compare(list.value[i], item) === 0) { return true; } } diff --git a/src/engine/blocks.js b/src/engine/blocks.js index ba9077fc..7a9ecbb8 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -260,7 +260,7 @@ class Blocks { // If not, create it on the stage. // TODO create global and local variables when UI provides a way. if (!optRuntime.getEditingTarget().lookupVariableById(e.varId)) { - stage.createVariable(e.varId, e.varName); + stage.createVariable(e.varId, e.varName, e.varType); } break; case 'var_rename': @@ -310,7 +310,7 @@ class Blocks { case 'field': // Update block value if (!block.fields[args.name]) return; - if (args.name === 'VARIABLE') { + if (args.name === 'VARIABLE' || args.name === 'LIST') { // Get variable name using the id in args.value. const variable = optRuntime.getEditingTarget().lookupVariableById(args.value); if (variable) { diff --git a/src/engine/execute.js b/src/engine/execute.js index a4bf76b2..8093a346 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -138,7 +138,7 @@ const execute = function (sequencer, thread) { // Add all fields on this block to the argValues. for (const fieldName in fields) { if (!fields.hasOwnProperty(fieldName)) continue; - if (fieldName === 'VARIABLE') { + if (fieldName === 'VARIABLE' || fieldName === 'LIST') { argValues[fieldName] = fields[fieldName].id; } else { argValues[fieldName] = fields[fieldName].value; diff --git a/src/engine/list.js b/src/engine/list.js deleted file mode 100644 index 0f3ec0f2..00000000 --- a/src/engine/list.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @fileoverview - * Object representing a Scratch list. - */ - -/** - * @param {!string} name Name of the list. - * @param {Array} contents Contents of the list, as an array. - * @constructor - */ -class List { - constructor (name, contents) { - this.name = name; - this.contents = contents; - } -} - -module.exports = List; diff --git a/src/engine/target.js b/src/engine/target.js index 2a6be81a..764094da 100644 --- a/src/engine/target.js +++ b/src/engine/target.js @@ -2,7 +2,6 @@ const EventEmitter = require('events'); const Blocks = require('./blocks'); const Variable = require('../engine/variable'); -const List = require('../engine/list'); const uid = require('../util/uid'); const {Map} = require('immutable'); @@ -88,7 +87,7 @@ class Target extends EventEmitter { const variable = this.lookupVariableById(id); if (variable) return variable; // No variable with this name exists - create it locally. - const newVariable = new Variable(id, name, 0, false); + const newVariable = new Variable(id, name, "", false); this.variables[id] = newVariable; return newVariable; } @@ -117,24 +116,16 @@ class Target extends EventEmitter { /** * Look up a list object for this target, and create it if one doesn't exist. * Search begins for local lists; then look for globals. + * @param {!string} id Id of the list. * @param {!string} name Name of the list. * @return {!List} List object. */ - lookupOrCreateList (name) { - // If we have a local copy, return it. - if (this.lists.hasOwnProperty(name)) { - return this.lists[name]; - } - // If the stage has a global copy, return it. - if (this.runtime && !this.isStage) { - const stage = this.runtime.getTargetForStage(); - if (stage.lists.hasOwnProperty(name)) { - return stage.lists[name]; - } - } - // No list with this name exists - create it locally. - const newList = new List(name, []); - this.lists[name] = newList; + lookupOrCreateList (id, name) { + const list = this.lookupVariableById(id); + if (list) return list; + // No variable with this name exists - create it locally. + const newList = new Variable(id, name, "list", false); + this.variables[id] = newList; return newList; } @@ -143,11 +134,11 @@ class Target extends EventEmitter { * dictionary of variables. * @param {string} id Id of variable * @param {string} name Name of variable. + * @param {string} type Type of variable, one of string, number, list */ - createVariable (id, name) { + createVariable (id, name, type) { if (!this.variables.hasOwnProperty(id)) { - const newVariable = new Variable(id, name, 0, - false); + const newVariable = new Variable(id, name, type, false); this.variables[id] = newVariable; } } diff --git a/src/engine/variable.js b/src/engine/variable.js index b79ab297..9f047a73 100644 --- a/src/engine/variable.js +++ b/src/engine/variable.js @@ -9,19 +9,27 @@ class Variable { /** * @param {string} id Id of the variable. * @param {string} name Name of the variable. - * @param {(string|number)} value Value of the variable. + * @param {(string|number)} type Type of the variable, one of "" or "list" * @param {boolean} isCloud Whether the variable is stored in the cloud. * @constructor */ - constructor (id, name, value, isCloud) { + constructor (id, name, type, isCloud) { this.id = id || uid(); this.name = name; - this.value = value; + this.type = type; this.isCloud = isCloud; + switch (this.type) { + case "": + this.value = 0; + break; + case "list": + this.value = []; + break; + } } toXML () { - return `${this.name}`; + return `${this.name}`; } } diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index 2a54d40a..e76d6ee2 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -13,7 +13,6 @@ const log = require('../util/log'); const uid = require('../util/uid'); const specMap = require('./sb2_specmap'); const Variable = require('../engine/variable'); -const List = require('../engine/list'); const {loadCostume} = require('../import/load-costume.js'); const {loadSound} = require('../import/load-sound.js'); @@ -235,9 +234,10 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) { const newVariable = new Variable( getVariableId(variable.name), variable.name, - variable.value, + "", variable.isPersistent ); + newVariable.value = variable.value; target.variables[newVariable.id] = newVariable; } } @@ -251,10 +251,14 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) { for (let k = 0; k < object.lists.length; k++) { const list = object.lists[k]; // @todo: monitor properties. - target.lists[list.listName] = new List( + const newVariable = new Variable( + getVariableId(list.listName), list.listName, - list.contents + "list", + list.isPersistent ); + newVariable.value = list.contents; + target.variables[newVariable.id] = newVariable; } } if (object.hasOwnProperty('scratchX')) { diff --git a/src/serialization/sb2_specmap.js b/src/serialization/sb2_specmap.js index cf5be1c8..59200a06 100644 --- a/src/serialization/sb2_specmap.js +++ b/src/serialization/sb2_specmap.js @@ -1252,7 +1252,7 @@ const specMap = { ] }, 'contentsOfList:': { - opcode: 'data_list', + opcode: 'data_listcontents', argMap: [ { type: 'field', diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 426f1004..9e8bcbf4 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -8,7 +8,6 @@ const vmPackage = require('../../package.json'); const Blocks = require('../engine/blocks'); const Sprite = require('../sprites/sprite'); const Variable = require('../engine/variable'); -const List = require('../engine/list'); const {loadCostume} = require('../import/load-costume.js'); const {loadSound} = require('../import/load-sound.js'); @@ -125,22 +124,13 @@ const parseScratchObject = function (object, runtime, extensions) { const newVariable = new Variable( variable.id, variable.name, - variable.value, + variable.type, variable.isPersistent ); + newVariable.value = variable.value; target.variables[newVariable.id] = newVariable; } } - if (object.hasOwnProperty('lists')) { - for (let k = 0; k < object.lists.length; k++) { - const list = object.lists[k]; - // @todo: monitor properties. - target.lists[list.listName] = new List( - list.listName, - list.contents - ); - } - } if (object.hasOwnProperty('x')) { target.x = object.x; } From db3b0ca204f0410dc2c751d98d20a9eba49ace95 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 9 Nov 2017 17:41:37 -0500 Subject: [PATCH 572/721] Move musical timing into music extension and use the stackframe timer, which improves timing accuracy. --- src/blocks/scratch3_music.js | 55 ++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/src/blocks/scratch3_music.js b/src/blocks/scratch3_music.js index f70fd48a..80232f00 100644 --- a/src/blocks/scratch3_music.js +++ b/src/blocks/scratch3_music.js @@ -3,6 +3,7 @@ const BlockType = require('../extension-support/block-type'); const Clone = require('../util/clone'); const Cast = require('../util/cast'); const MathUtil = require('../util/math-util'); +const Timer = require('../util/timer'); /** * An array of drum names, used in the play drum block. @@ -70,6 +71,13 @@ class Scratch3MusicBlocks { */ this.runtime = runtime; + /** + * The current tempo in beats per minute. The tempo is a global property of the project, + * not a property of each sprite, so it is not stored in the MusicState object. + * @type {number} + */ + this.tempo = 60; + this.drumMenu = this._buildMenu(drumNames); this.instrumentMenu = this._buildMenu(instrumentNames); } @@ -272,11 +280,10 @@ class Scratch3MusicBlocks { * @property {number} BEATS - the duration in beats of the rest. * @return {Promise} - a promise which will resolve at the end of the duration. */ - restForBeats (args) { + restForBeats (args, util) { let beats = Cast.toNumber(args.BEATS); beats = this._clampBeats(beats); - if (typeof this.runtime.audioEngine === 'undefined') return; - return this.runtime.audioEngine.waitForBeats(beats); + this._waitForBeats(beats, util); } /** @@ -315,6 +322,39 @@ class Scratch3MusicBlocks { return this.runtime.audioEngine.instrumentPlayer.loadInstrument(musicState.currentInstrument); } + /** + * Convert a number of beats to a number of seconds, using the current tempo. + * @param {number} beats - number of beats to convert to secs. + * @return {number} seconds - number of seconds `beats` will last. + * @private + */ + _beatsToSec (beats) { + return (60 / this.tempo) * beats; + } + + /** + * Wait for some number of beats. + * @param {number} beats - number of beats to wait for. + * @param {object} util - utility object provided by the runtime. + * @private + */ + _waitForBeats (beats, util) { + if (util.stackFrame.timer) { + const timeElapsed = util.stackFrame.timer.timeElapsed(); + if (timeElapsed < util.stackFrame.duration * 1000) { + util.yield(); + } + } else { + util.stackFrame.timer = new Timer(); + util.stackFrame.timer.start(); + util.stackFrame.duration = this._beatsToSec(beats); + if (util.stackFrame.duration <= 0) { + return; + } + util.yield(); + } + } + /** * Clamp a duration in beats to the allowed min and max duration. * @param {number} beats - a duration in beats. @@ -342,8 +382,7 @@ class Scratch3MusicBlocks { */ changeTempo (args) { const change = Cast.toNumber(args.TEMPO); - if (typeof this.runtime.audioEngine === 'undefined') return; - const tempo = change + this.runtime.audioEngine.currentTempo; + const tempo = change + this.tempo; this._updateTempo(tempo); } @@ -354,8 +393,7 @@ class Scratch3MusicBlocks { */ _updateTempo (tempo) { tempo = MathUtil.clamp(tempo, Scratch3MusicBlocks.TEMPO_RANGE.min, Scratch3MusicBlocks.TEMPO_RANGE.max); - if (typeof this.runtime.audioEngine === 'undefined') return; - this.runtime.audioEngine.setTempo(tempo); + this.tempo = tempo; } /** @@ -363,8 +401,7 @@ class Scratch3MusicBlocks { * @return {number} - the current tempo, in beats per minute. */ getTempo () { - if (typeof this.runtime.audioEngine === 'undefined') return; - return this.runtime.audioEngine.currentTempo; + return this.tempo; } } From be25cd93bdfaae3b80e6151d7d59a1fdab460cf4 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 13 Nov 2017 10:43:09 -0500 Subject: [PATCH 573/721] Use stack timer for timing of drum, note and rest blocks. --- src/blocks/scratch3_music.js | 158 +++++++++++++++++++++-------------- 1 file changed, 93 insertions(+), 65 deletions(-) diff --git a/src/blocks/scratch3_music.js b/src/blocks/scratch3_music.js index 80232f00..5cccaf27 100644 --- a/src/blocks/scratch3_music.js +++ b/src/blocks/scratch3_music.js @@ -260,17 +260,22 @@ class Scratch3MusicBlocks { * @param {object} util - utility object provided by the runtime. * @property {int} DRUM - the number of the drum to play. * @property {number} BEATS - the duration in beats of the drum sound. - * @return {Promise} - a promise which will resolve at the end of the duration. */ playDrumForBeats (args, util) { - let drum = Cast.toNumber(args.DRUM); - drum -= 1; // drums are one-indexed - if (typeof this.runtime.audioEngine === 'undefined') return; - drum = MathUtil.wrapClamp(drum, 0, this.runtime.audioEngine.numDrums - 1); - let beats = Cast.toNumber(args.BEATS); - beats = this._clampBeats(beats); - if (util.target.audioPlayer === null) return; - return util.target.audioPlayer.playDrumForBeats(drum, beats); + if (this._stackTimerNeedsInit(util)) { + let drum = Cast.toNumber(args.DRUM); + drum -= 1; // drums are one-indexed + if (typeof this.runtime.audioEngine === 'undefined') return; + drum = MathUtil.wrapClamp(drum, 0, this.runtime.audioEngine.numDrums - 1); + let beats = Cast.toNumber(args.BEATS); + beats = this._clampBeats(beats); + if (util.target.audioPlayer !== null) { + util.target.audioPlayer.playDrumForBeats(drum, beats); + } + this._startStackTimer(util, this._beatsToSec(beats)); + } else { + this._checkStackTimer(util); + } } /** @@ -278,12 +283,15 @@ class Scratch3MusicBlocks { * @param {object} args - the block arguments. * @param {object} util - utility object provided by the runtime. * @property {number} BEATS - the duration in beats of the rest. - * @return {Promise} - a promise which will resolve at the end of the duration. */ restForBeats (args, util) { - let beats = Cast.toNumber(args.BEATS); - beats = this._clampBeats(beats); - this._waitForBeats(beats, util); + if (this._stackTimerNeedsInit(util)) { + let beats = Cast.toNumber(args.BEATS); + beats = this._clampBeats(beats); + this._startStackTimer(util, this._beatsToSec(beats)); + } else { + this._checkStackTimer(util); + } } /** @@ -292,17 +300,80 @@ class Scratch3MusicBlocks { * @param {object} util - utility object provided by the runtime. * @property {number} NOTE - the pitch of the note to play, interpreted as a MIDI note number. * @property {number} BEATS - the duration in beats of the note. - * @return {Promise} - a promise which will resolve at the end of the duration. */ playNoteForBeats (args, util) { - let note = Cast.toNumber(args.NOTE); - note = MathUtil.clamp(note, Scratch3MusicBlocks.MIDI_NOTE_RANGE.min, Scratch3MusicBlocks.MIDI_NOTE_RANGE.max); - let beats = Cast.toNumber(args.BEATS); - beats = this._clampBeats(beats); - const musicState = this._getMusicState(util.target); - const inst = musicState.currentInstrument; - if (typeof this.runtime.audioEngine === 'undefined') return; - return this.runtime.audioEngine.playNoteForBeatsWithInstAndVol(note, beats, inst, 100); + if (this._stackTimerNeedsInit(util)) { + let note = Cast.toNumber(args.NOTE); + note = MathUtil.clamp(note, + Scratch3MusicBlocks.MIDI_NOTE_RANGE.min, Scratch3MusicBlocks.MIDI_NOTE_RANGE.max); + let beats = Cast.toNumber(args.BEATS); + beats = this._clampBeats(beats); + const musicState = this._getMusicState(util.target); + const inst = musicState.currentInstrument; + if (typeof this.runtime.audioEngine !== 'undefined') { + this.runtime.audioEngine.playNoteForBeatsWithInstAndVol(note, beats, inst, 100); + } + this._startStackTimer(util, this._beatsToSec(beats)); + } else { + this._checkStackTimer(util); + } + } + + /** + * Clamp a duration in beats to the allowed min and max duration. + * @param {number} beats - a duration in beats. + * @return {number} - the clamped duration. + * @private + */ + _clampBeats (beats) { + return MathUtil.clamp(beats, Scratch3MusicBlocks.BEAT_RANGE.min, Scratch3MusicBlocks.BEAT_RANGE.max); + } + + /** + * Convert a number of beats to a number of seconds, using the current tempo. + * @param {number} beats - number of beats to convert to secs. + * @return {number} seconds - number of seconds `beats` will last. + * @private + */ + _beatsToSec (beats) { + return (60 / this.tempo) * beats; + } + + /** + * Check if the stack timer needs initialization. + * @param {object} util - utility object provided by the runtime. + * @return {boolean} - true if the stack timer needs to be initialized. + * @private + */ + _stackTimerNeedsInit (util) { + return !util.stackFrame.timer; + } + + /** + * Start the stack timer and the yield the thread if necessary. + * @param {object} util - utility object provided by the runtime. + * @param {number} duration - a duration in seconds to set the timer for. + * @private + */ + _startStackTimer (util, duration) { + util.stackFrame.timer = new Timer(); + util.stackFrame.timer.start(); + util.stackFrame.duration = duration; + if (util.stackFrame.duration > 0) { + util.yield(); + } + } + + /** + * Check the stack timer, and if its time is not up yet, yield the thread. + * @param {object} util - utility object provided by the runtime. + * @private + */ + _checkStackTimer (util) { + const timeElapsed = util.stackFrame.timer.timeElapsed(); + if (timeElapsed < util.stackFrame.duration * 1000) { + util.yield(); + } } /** @@ -322,49 +393,6 @@ class Scratch3MusicBlocks { return this.runtime.audioEngine.instrumentPlayer.loadInstrument(musicState.currentInstrument); } - /** - * Convert a number of beats to a number of seconds, using the current tempo. - * @param {number} beats - number of beats to convert to secs. - * @return {number} seconds - number of seconds `beats` will last. - * @private - */ - _beatsToSec (beats) { - return (60 / this.tempo) * beats; - } - - /** - * Wait for some number of beats. - * @param {number} beats - number of beats to wait for. - * @param {object} util - utility object provided by the runtime. - * @private - */ - _waitForBeats (beats, util) { - if (util.stackFrame.timer) { - const timeElapsed = util.stackFrame.timer.timeElapsed(); - if (timeElapsed < util.stackFrame.duration * 1000) { - util.yield(); - } - } else { - util.stackFrame.timer = new Timer(); - util.stackFrame.timer.start(); - util.stackFrame.duration = this._beatsToSec(beats); - if (util.stackFrame.duration <= 0) { - return; - } - util.yield(); - } - } - - /** - * Clamp a duration in beats to the allowed min and max duration. - * @param {number} beats - a duration in beats. - * @return {number} - the clamped duration. - * @private - */ - _clampBeats (beats) { - return MathUtil.clamp(beats, Scratch3MusicBlocks.BEAT_RANGE.min, Scratch3MusicBlocks.BEAT_RANGE.max); - } - /** * Set the current tempo to a new value. * @param {object} args - the block arguments. From 37d627bc8955c9fa7d9323e042fceeb3fb81a874 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 13 Nov 2017 10:57:36 -0500 Subject: [PATCH 574/721] Fix unit test --- test/unit/blocks_music.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/blocks_music.js b/test/unit/blocks_music.js index 3b8f8d94..ae097beb 100644 --- a/test/unit/blocks_music.js +++ b/test/unit/blocks_music.js @@ -17,7 +17,8 @@ const util = { audioPlayer: { playDrumForBeats: drum => (playedDrum = drum) } - } + }, + stackFrame: Object.create(null) }; test('playDrum uses 1-indexing and wrap clamps', t => { From b18938963fab4b6ec4c74c861b14b35d0a97da2a Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 13 Nov 2017 14:22:36 -0500 Subject: [PATCH 575/721] Fixing linting errors --- src/engine/target.js | 4 ++-- src/engine/variable.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engine/target.js b/src/engine/target.js index 764094da..ebe7d495 100644 --- a/src/engine/target.js +++ b/src/engine/target.js @@ -87,7 +87,7 @@ class Target extends EventEmitter { const variable = this.lookupVariableById(id); if (variable) return variable; // No variable with this name exists - create it locally. - const newVariable = new Variable(id, name, "", false); + const newVariable = new Variable(id, name, '', false); this.variables[id] = newVariable; return newVariable; } @@ -124,7 +124,7 @@ class Target extends EventEmitter { const list = this.lookupVariableById(id); if (list) return list; // No variable with this name exists - create it locally. - const newList = new Variable(id, name, "list", false); + const newList = new Variable(id, name, 'list', false); this.variables[id] = newList; return newList; } diff --git a/src/engine/variable.js b/src/engine/variable.js index 9f047a73..55617894 100644 --- a/src/engine/variable.js +++ b/src/engine/variable.js @@ -19,10 +19,10 @@ class Variable { this.type = type; this.isCloud = isCloud; switch (this.type) { - case "": + case '': this.value = 0; break; - case "list": + case 'list': this.value = []; break; } From 038a65b460cfa7772b05867c907c24062060fda3 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 13 Nov 2017 14:24:30 -0500 Subject: [PATCH 576/721] Finishing up lists. --- src/engine/adapter.js | 4 +++ src/engine/blocks.js | 11 +++++++- src/serialization/sb2.js | 10 ++++--- src/serialization/sb2_specmap.js | 45 +++++++++++++++++++++----------- 4 files changed, 51 insertions(+), 19 deletions(-) diff --git a/src/engine/adapter.js b/src/engine/adapter.js index ffb8d043..a8534e24 100644 --- a/src/engine/adapter.js +++ b/src/engine/adapter.js @@ -75,6 +75,10 @@ const domToBlock = function (blockDOM, blocks, isTopBlock, parent) { id: fieldId, value: fieldData }; + const fieldVarType = xmlChild.attribs.variabletype; + if (typeof fieldVarType === 'string') { + block.fields[fieldName].variableType = fieldVarType; + } break; } case 'value': diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 7a9ecbb8..bace2f1b 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -499,11 +499,20 @@ class Blocks { for (const field in block.fields) { if (!block.fields.hasOwnProperty(field)) continue; const blockField = block.fields[field]; + xmlString += `${value}`; + xmlString += `>${value}`; } // Add blocks connected to the next connection. if (block.next) { diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index e76d6ee2..0c6c3781 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -234,7 +234,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) { const newVariable = new Variable( getVariableId(variable.name), variable.name, - "", + '', variable.isPersistent ); newVariable.value = variable.value; @@ -254,7 +254,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) { const newVariable = new Variable( getVariableId(list.listName), list.listName, - "list", + 'list', list.isPersistent ); newVariable.value = list.contents; @@ -490,10 +490,14 @@ const parseBlock = function (sb2block, getVariableId, extensions) { value: providedArg }; - if (expectedArg.fieldName === 'VARIABLE') { + if (expectedArg.fieldName === 'VARIABLE' || expectedArg.fieldName === 'LIST') { // Add `id` property to variable fields activeBlock.fields[expectedArg.fieldName].id = getVariableId(providedArg); } + const varType = expectedArg.variableType; + if (typeof varType === 'string') { + activeBlock.fields[expectedArg.fieldName].variableType = varType; + } } } // Special cases to generate mutations. diff --git a/src/serialization/sb2_specmap.js b/src/serialization/sb2_specmap.js index 59200a06..c7de538e 100644 --- a/src/serialization/sb2_specmap.js +++ b/src/serialization/sb2_specmap.js @@ -1201,7 +1201,8 @@ const specMap = { argMap: [ { type: 'field', - fieldName: 'VARIABLE' + fieldName: 'VARIABLE', + variableType: '' } ] }, @@ -1210,7 +1211,8 @@ const specMap = { argMap: [ { type: 'field', - fieldName: 'VARIABLE' + fieldName: 'VARIABLE', + variableType: '' }, { type: 'input', @@ -1224,7 +1226,8 @@ const specMap = { argMap: [ { type: 'field', - fieldName: 'VARIABLE' + fieldName: 'VARIABLE', + variableType: '' }, { type: 'input', @@ -1238,7 +1241,8 @@ const specMap = { argMap: [ { type: 'field', - fieldName: 'VARIABLE' + fieldName: 'VARIABLE', + variableType: '' } ] }, @@ -1247,7 +1251,8 @@ const specMap = { argMap: [ { type: 'field', - fieldName: 'VARIABLE' + fieldName: 'VARIABLE', + variableType: '' } ] }, @@ -1256,7 +1261,8 @@ const specMap = { argMap: [ { type: 'field', - fieldName: 'LIST' + fieldName: 'LIST', + variableType: 'list' } ] }, @@ -1270,7 +1276,8 @@ const specMap = { }, { type: 'field', - fieldName: 'LIST' + fieldName: 'LIST', + variableType: 'list' } ] }, @@ -1284,7 +1291,8 @@ const specMap = { }, { type: 'field', - fieldName: 'LIST' + fieldName: 'LIST', + variableType: 'list' } ] }, @@ -1303,7 +1311,8 @@ const specMap = { }, { type: 'field', - fieldName: 'LIST' + fieldName: 'LIST', + variableType: 'list' } ] }, @@ -1317,7 +1326,8 @@ const specMap = { }, { type: 'field', - fieldName: 'LIST' + fieldName: 'LIST', + variableType: 'list' }, { type: 'input', @@ -1336,7 +1346,8 @@ const specMap = { }, { type: 'field', - fieldName: 'LIST' + fieldName: 'LIST', + variableType: 'list' } ] }, @@ -1345,7 +1356,8 @@ const specMap = { argMap: [ { type: 'field', - fieldName: 'LIST' + fieldName: 'LIST', + variableType: 'list' } ] }, @@ -1354,7 +1366,8 @@ const specMap = { argMap: [ { type: 'field', - fieldName: 'LIST' + fieldName: 'LIST', + variableType: 'list' }, { type: 'input', @@ -1368,7 +1381,8 @@ const specMap = { argMap: [ { type: 'field', - fieldName: 'LIST' + fieldName: 'LIST', + variableType: 'list' } ] }, @@ -1377,7 +1391,8 @@ const specMap = { argMap: [ { type: 'field', - fieldName: 'LIST' + fieldName: 'LIST', + variableType: 'list' } ] }, From 1d4acde7dcc1114117ad717714c31952b935c7ef Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 13 Nov 2017 14:25:12 -0500 Subject: [PATCH 577/721] Fixing unit/integration tests. --- test/integration/hat-execution-order.js | 3 ++- test/unit/engine_target.js | 31 ++++++++++++++++++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/test/integration/hat-execution-order.js b/test/integration/hat-execution-order.js index c8d2dcab..3e5e92a2 100644 --- a/test/integration/hat-execution-order.js +++ b/test/integration/hat-execution-order.js @@ -16,7 +16,8 @@ test('complex', t => { const threads = JSON.parse(e.threads); t.ok(threads.length === 0); - const results = vm.runtime.targets[0].lists.results.contents; + const resultKey = Object.keys(vm.runtime.targets[0].variables)[0]; + const results = vm.runtime.targets[0].variables[resultKey].value; t.deepEqual(results, ['3', '2', '1', 'stage']); t.end(); diff --git a/test/unit/engine_target.js b/test/unit/engine_target.js index cec5cf16..db5b5e76 100644 --- a/test/unit/engine_target.js +++ b/test/unit/engine_target.js @@ -23,13 +23,14 @@ test('spec', t => { // Create Variable tests. test('createVariable', t => { const target = new Target(); - target.createVariable('foo', 'bar'); + target.createVariable('foo', 'bar', ''); const variables = target.variables; t.equal(Object.keys(variables).length, 1); const variable = variables[Object.keys(variables)[0]]; t.equal(variable.id, 'foo'); t.equal(variable.name, 'bar'); + t.equal(variable.type, ''); t.equal(variable.value, 0); t.equal(variable.isCloud, false); @@ -39,8 +40,8 @@ test('createVariable', t => { // Create Same Variable twice. test('createVariable2', t => { const target = new Target(); - target.createVariable('foo', 'bar'); - target.createVariable('foo', 'bar'); + target.createVariable('foo', 'bar', ''); + target.createVariable('foo', 'bar', ''); const variables = target.variables; t.equal(Object.keys(variables).length, 1); @@ -48,10 +49,28 @@ test('createVariable2', t => { t.end(); }); +// Create a list +test('createVariable creates a list', t => { + const target = new Target(); + target.createVariable('foo', 'bar', 'list'); + + const variables = target.variables; + t.equal(Object.keys(variables).length, 1); + const variable = variables[Object.keys(variables)[0]]; + t.equal(variable.id, 'foo'); + t.equal(variable.name, 'bar'); + t.equal(variable.type, 'list'); + t.assert(variable.value instanceof Array, true); + t.equal(variable.value.length, 0); + t.equal(variable.isCloud, false); + + t.end(); +}); + // Rename Variable tests. test('renameVariable', t => { const target = new Target(); - target.createVariable('foo', 'bar'); + target.createVariable('foo', 'bar', ''); target.renameVariable('foo', 'bar2'); const variables = target.variables; @@ -80,7 +99,7 @@ test('renameVariable2', t => { // Expect no change. test('renameVariable3', t => { const target = new Target(); - target.createVariable('foo1', 'foo'); + target.createVariable('foo1', 'foo', ''); target.renameVariable('foo', 'bar2'); const variables = target.variables; @@ -95,7 +114,7 @@ test('renameVariable3', t => { // Delete Variable tests. test('deleteVariable', t => { const target = new Target(); - target.createVariable('foo', 'bar'); + target.createVariable('foo', 'bar', ''); target.deleteVariable('foo'); const variables = target.variables; From 961b3476b20852c58d8c1f131cc1081a73ef4347 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 13 Nov 2017 16:22:37 -0500 Subject: [PATCH 578/721] Move the music extension into a new extensions directory --- src/extension-support/extension-manager.js | 2 +- .../scratch3_music/index.js} | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) rename src/{blocks/scratch3_music.js => extensions/scratch3_music/index.js} (97%) diff --git a/src/extension-support/extension-manager.js b/src/extension-support/extension-manager.js index 938e791d..c3346a22 100644 --- a/src/extension-support/extension-manager.js +++ b/src/extension-support/extension-manager.js @@ -8,7 +8,7 @@ const BlockType = require('./block-type'); // TODO: change extension spec so that library info, including extension ID, can be collected through static methods const Scratch3PenBlocks = require('../blocks/scratch3_pen'); const Scratch3WeDo2Blocks = require('../blocks/scratch3_wedo2'); -const Scratch3MusicBlocks = require('../blocks/scratch3_music'); +const Scratch3MusicBlocks = require('../extensions/scratch3_music'); const builtinExtensions = { pen: Scratch3PenBlocks, wedo2: Scratch3WeDo2Blocks, diff --git a/src/blocks/scratch3_music.js b/src/extensions/scratch3_music/index.js similarity index 97% rename from src/blocks/scratch3_music.js rename to src/extensions/scratch3_music/index.js index 5cccaf27..ba136003 100644 --- a/src/blocks/scratch3_music.js +++ b/src/extensions/scratch3_music/index.js @@ -1,9 +1,9 @@ -const ArgumentType = require('../extension-support/argument-type'); -const BlockType = require('../extension-support/block-type'); -const Clone = require('../util/clone'); -const Cast = require('../util/cast'); -const MathUtil = require('../util/math-util'); -const Timer = require('../util/timer'); +const ArgumentType = require('../../extension-support/argument-type'); +const BlockType = require('../../extension-support/block-type'); +const Clone = require('../../util/clone'); +const Cast = require('../../util/cast'); +const MathUtil = require('../../util/math-util'); +const Timer = require('../../util/timer'); /** * An array of drum names, used in the play drum block. From f5b25a190e3c41185941fbc36251dea7fbe5d84f Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 13 Nov 2017 16:22:58 -0500 Subject: [PATCH 579/721] Add assets (drum sounds) for music extension --- .../scratch3_music/assets/1-snare.mp3 | Bin 0 -> 5557 bytes .../scratch3_music/assets/10-wood-block.mp3 | Bin 0 -> 3676 bytes .../scratch3_music/assets/11-cowbell.mp3 | Bin 0 -> 2688 bytes .../scratch3_music/assets/12-triangle.mp3 | Bin 0 -> 10455 bytes .../scratch3_music/assets/13-bongo.mp3 | Bin 0 -> 5269 bytes .../scratch3_music/assets/14-conga.mp3 | Bin 0 -> 3808 bytes .../scratch3_music/assets/15-cabasa.mp3 | Bin 0 -> 2041 bytes .../scratch3_music/assets/16-guiro.mp3 | Bin 0 -> 4905 bytes .../scratch3_music/assets/17-vibraslap.mp3 | Bin 0 -> 17089 bytes .../scratch3_music/assets/18-cuica.mp3 | Bin 0 -> 3257 bytes .../scratch3_music/assets/2-bass-drum.mp3 | Bin 0 -> 4513 bytes .../scratch3_music/assets/3-side-stick.mp3 | Bin 0 -> 3408 bytes .../scratch3_music/assets/4-crash-cymbal.mp3 | Bin 0 -> 17392 bytes .../scratch3_music/assets/5-open-hi-hat.mp3 | Bin 0 -> 6414 bytes .../scratch3_music/assets/6-closed-hi-hat.mp3 | Bin 0 -> 3338 bytes .../scratch3_music/assets/7-tambourine.mp3 | Bin 0 -> 4434 bytes .../scratch3_music/assets/8-hand-clap.mp3 | Bin 0 -> 2582 bytes .../scratch3_music/assets/9-claves.mp3 | Bin 0 -> 2973 bytes webpack.config.js | 8 +++++++- 19 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 src/extensions/scratch3_music/assets/1-snare.mp3 create mode 100644 src/extensions/scratch3_music/assets/10-wood-block.mp3 create mode 100644 src/extensions/scratch3_music/assets/11-cowbell.mp3 create mode 100644 src/extensions/scratch3_music/assets/12-triangle.mp3 create mode 100644 src/extensions/scratch3_music/assets/13-bongo.mp3 create mode 100644 src/extensions/scratch3_music/assets/14-conga.mp3 create mode 100644 src/extensions/scratch3_music/assets/15-cabasa.mp3 create mode 100644 src/extensions/scratch3_music/assets/16-guiro.mp3 create mode 100644 src/extensions/scratch3_music/assets/17-vibraslap.mp3 create mode 100644 src/extensions/scratch3_music/assets/18-cuica.mp3 create mode 100644 src/extensions/scratch3_music/assets/2-bass-drum.mp3 create mode 100644 src/extensions/scratch3_music/assets/3-side-stick.mp3 create mode 100644 src/extensions/scratch3_music/assets/4-crash-cymbal.mp3 create mode 100644 src/extensions/scratch3_music/assets/5-open-hi-hat.mp3 create mode 100644 src/extensions/scratch3_music/assets/6-closed-hi-hat.mp3 create mode 100644 src/extensions/scratch3_music/assets/7-tambourine.mp3 create mode 100644 src/extensions/scratch3_music/assets/8-hand-clap.mp3 create mode 100644 src/extensions/scratch3_music/assets/9-claves.mp3 diff --git a/src/extensions/scratch3_music/assets/1-snare.mp3 b/src/extensions/scratch3_music/assets/1-snare.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..38970d352e35a6524e09e0832bb265772eba3542 GIT binary patch literal 5557 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 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/10-wood-block.mp3 b/src/extensions/scratch3_music/assets/10-wood-block.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..9c551b3661873ca96cc76af6df131f648c0e8e4b GIT binary patch literal 3676 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 kJN}<d0LlPxBn|)s@&7yj28DZ=+W-In literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/12-triangle.mp3 b/src/extensions/scratch3_music/assets/12-triangle.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0987759cae7299681033cb17d65b48169c5029eb GIT binary patch literal 10455 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

i9^;kLV^qMP4j8A{*eqoPiaywSLtaWt2b zi+!5L8Fy2DQ)5N{kg~#cFEr!aW}zG}J1>Bna-t@%T29iBl1g^*0vX@^WwPvS(Wn7K zv_Q&8&q?EG&}_<3TSCR~qHeX{%{D&ebY27`I^0{NQq)R!1Y|hA6Y&(Rl-#U3ZNYhc z7ZNN*kJg%;g&4oRl2O`|(HuzgEn4+KXf!LN5OdyJ1Ia0y;-31r8ku`Wd_$<@V(t1< z)>rnTq**qN>UQ$CIBO#E_s-&rYV9Z7;|FEqCQ4haJ@JXEb>%%&*OQG)*dA!IdIC^U zJ~UvRbJe5Qn^0rkT%lPr|3z zZ+K1yzgW|`K4RY-MrhIWRT$m2z5!OHmHXbJg|EEQ#?$V?>HL@BUkrhBnL+M4csExO z-zEC9CyhG`u!Tlh6DpbN)L=ihpYcYV;R{J*&&ulVYE!P)aVgQ=aa~sDcRTZ)Ln~DU z;dHtGy|qyBK!m}saTH`nLNg zv!k#qP*1;9mN8qLK}Z22#H4Jf&{-xU9U=9BM4b1TJ=iwyXx)MAS31x_!vU&#!By8h zPszLfASGPNF3;_i0#*~3AGUKZ(=A8e-gJ?p*OR>H1D#ZLtLuD!Xqq}eNnm)D`b zbMD3E{)B_WU>xIjg}AnD{k`bbcRyxZh|cMfSt|OrihxPm{M6f0y2>%*@tat&8E|oW zwZF9dN#mFSRRgGJy)+8=uSAHmXb6+la zMI$3?7p$4n+hlD&C>B}QFx&A_>iP492YEbZHZA6cODv?}djrwKX<_57m8GK}8j_e= zzFve4U4bCx=ltjLaUa8$G_D0?H10V;<(Nkwr@wx!mP=j=DxS;txqY{~K4_G3@zONU6EzRF zB1A*kJObW7-_#V0>#&+LNfBWZ#Aee_d$(Z^ykW18ptg;E&PckH!XvC_x6KS=A4n+xDmIm9})r5IZ9gZ z$Oq44XuLSRvqa6^eJ_3b$A6yK@uO&4!>n!e;3eaY`y_U2A!^V$8^S!*| z&(N3#s+{lDEbEz;C5ZK}?DW3Wz6y#91Xo}|@OePZV$UCm|ouBT0JzK59 zZ_(kC^soV}%3S7TiJ&_E$H(arlZ;#pd9=YFi)h!A(UqyCm-M(mts)Z|beAD(@eaEi z$Y3f{gbJ$Do03m{?E#7wPqUHr8%@thHFnRiQWLGpe}RkfyoG)es42DW9AlL*(RZ*( zHZa>=Izv|sXd2e9%V9L(CVVCr_k)~Yf|)wBELp`xK_V~^`y(xv+j8&REH<i zOl@wIg_|UQ$&r%$h(ZiWNx0UblzZD3n4qt!Uk@W;oDOqiK(* zG?u9^a|^;x&)RShP^Dw7+=xb4DHt0S>3eY>Ia$-s$HyAl(^yUy0%zm0IvZw$GdM{P z3&0vvZc642T%;k?Y85caJCdwvyBwV0ui_ISqHoui)|Ap@EoCQlz=iH$F?nmgRy8Aj z9}l(bo_Tj@?$vS=9~f&a%dIC$p)uJrh}dBp8{K6>4O45bv&#=y?j@aB&R)44sDg^ zIdoG8XYpOEo?B%ipHj=8R8AMAN?p@$XEv^RHt8z!40}2FY^ezC}jtNw-es2Hc*Pp*ta!bB%o3Ep!TPZgC zg@mo09-=^#gY9-|G2JbAqT^feb&YPK06b2by?)Ve?!$jycnT+VXp*`Bs*2-Vya)g& Mp^xjp>i*CD2b*N8kpKVy literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/15-cabasa.mp3 b/src/extensions/scratch3_music/assets/15-cabasa.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..4fce061764374f78508d50cbc6e8e34c33a864b2 GIT binary patch literal 2041 zcmai#do&Y%AIEpiUBlE!$uE-$V_Thc`8~zvvbk)Fni(Ru`H4~}PvlZENV0B_>hCPFt;youJ$eBH#_W+a=C&LZ0$wbB0Km!WLU-r@fMdV*aJP5P|GV*jqC^b)bsz8=@E8CvpFO+5t4udLw+I6$NyLpp zr89Y<#TprJAj}`+#(6nOM5UqdOX$^(4VMotZWKt8f@2` zc#LRf`DEvRcSj+(+u}h=KvyO#c`KXQI#QY4)$5vQ3Jw^4T@6yj`q~zzg2{bQz&;Gx zJQn1l(bGyEOt3`ElFu@IDDj@bhU4}mzl}q4&lcAt9$6U&dBw@=&*VqqUk;2E+XSe4 z7LB683H))U-a#w(o;7QHd5q!b*-rli&Bg>RVmmbN?LfA>2)x2b)#+ADd7b*kGw@9E)%LU z81!7Zfkprs+5!Zpl@AoV6RePIhob?L1-Nupcf`XWL6(VV+N*vZGsHlDsH5fQHiu_V z`o*bZ=drZm*{aGQ!esd=mgv&xP*!@SpK@~%l%#8lntTXs5FuZK8{n5*KV8@!k%{uX zKji^5Ago8d6~p=UCx8Xe_-Ay?1hHbQ1x0zN+$j`ry8b8<&gn0!0|>Z(&FMDm_5@L5 zptFW1)oO%eZaAt9T<`Z8I$dDsOX#)_xs@oFwzhp;D{!m62~_hqwrh+2=UbgKFu0;F zgcF71SfA@?rwZnYvPyDZLi8O+Qv2659eK-BoNY(g?V%pBaGS3+^ydVVRuEf*PaWE4 z_;DY;zvA(j2ttEIT0m#5aSTknC1s-Uf`ev^Qkk&9yQl=AcSkE!vl-Tsql3h{Eq~`! zNNhqOc}#l1k(v`Je4Epudx6!TOERq)LiOI0=cY7pV60*3j^`roug3=1UZdu~{<-VD z!u!#ZI6JT;=(FF0qHv%TnCooaBec;`v(^#6pWBbF zybTGt{MJ=~CSD0#(@%G?@@!=!q6A;~5^3#xS(^x@ zbTD%A?ZzieZta`A&WcbZJ*_2FU{SB+m!l$7KiZLXgn5nd!X#9)?q{^8r*>1ljYGK{ z`V(n5oA?`pNVr&~co%4?NKRFt7$NfoZbXNcaO=01eAre$48t>Tym$okvm%}Qf#_`M z!&$@35;I!kz4!k{DxX5=n0xwad5?WEJ`RGG-)8wGU&nPN?$w2lY=LFMJdzgSE$8L% zxjXFObDRo(kN?e8ucZ*ZQch*9qx|CdX*pH?NxD`Wm4)Y?(llrdFvThNE8kUk!?C`eDnmC_6j5xr~TJcf(--DuRe2pNso2MKbsfIUnli#`#zt=PP1&MYVi zR697ah0S~a$_clus2ajejjp!)!yU8HTvxue?STOTAH7<*-s;BsR1uRf3VD|FFv`#y z;!5mQEIU-`++{rziS@-j;<{fxY{kRx>&CtQ(33;ZSEDi8c0H*2Dd z6<@Qu=5YT-iA20x2v;}_I-=bO0DQiq9!pgI?j7@h+(V2L@>#)13r}*NCS~St*E7a& Uxzhg#6Dmo4%lenUNzeb~PawRp;s5{u literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/16-guiro.mp3 b/src/extensions/scratch3_music/assets/16-guiro.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..882feaaafc039b527572a7d627076e5357dea5d8 GIT binary patch literal 4905 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 af?xlIrr$&EUmvM^g684+-HR9H$o~V00<`M@ literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/17-vibraslap.mp3 b/src/extensions/scratch3_music/assets/17-vibraslap.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..faf7315ae3db40ed431b40120a9feaff4ff3b6bd GIT binary patch literal 17089 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�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 H{tx*df?EMk literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/3-side-stick.mp3 b/src/extensions/scratch3_music/assets/3-side-stick.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..c7757c932b951d636278aac249344ac5d4b609d8 GIT binary patch literal 3408 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 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/5-open-hi-hat.mp3 b/src/extensions/scratch3_music/assets/5-open-hi-hat.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..2dc63fa30366ffeac6a86ef54c6cc95325258444 GIT binary patch literal 6414 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 X74ieXNkx+n0{~iOCAggB|CWCP{dmH7 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/7-tambourine.mp3 b/src/extensions/scratch3_music/assets/7-tambourine.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f0ea66a585ec62f281219a156336a9fb193fd8e0 GIT binary patch literal 4434 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 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/8-hand-clap.mp3 b/src/extensions/scratch3_music/assets/8-hand-clap.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..a5b55f522f628c09360473ca0429a1a41b3331c4 GIT binary patch literal 2582 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 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/9-claves.mp3 b/src/extensions/scratch3_music/assets/9-claves.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..75b76533dec423cece275edae9d2887073ed78ae GIT binary patch literal 2973 zcmd^>`#aP9AICq~$e}qO(up~ytmKrYl2cS$b7q9wF~V3zP8rEzrCTFxQ%+&lB+Vft zVIeKYsAR<$S?Dfz6l34Iuj{^C*Z2D;d>_~KzFyb$e*f@(U60of&zE35hY9>Q?jaGu zzwV@e9dQ7VyaT9iaeNE^Ei$&K+M;8N_gj45La@cJzffmSB5Vv{W=F7~=6_o%RXCNN7_505tp!!vqF7Kqj>)*g@_e1z-d`f*;xGzDx2L zv^fl=J1QbxXupM!G*4&orX}akx z2lsA0rI>flHKmzV+`Q3!VH(9UjEU8ysMbYf>?;&~uHW|rkG^5J*5fuAOej9$$F0KV zk7C9+vQJj&%l*ZY9-9gM3ln9Ho^#xD-s1SOOM_l~Waujz!=-wb$BgRv`M90kEckP} z=0iTqdZsbd+8E9ZAuEsLT} zIrFhYX`aL&Ms1z~t*5mWsRxzrzbp++eNZF)MT?pDv~S!y8^ z1*@K6!nBeutt@Tj7O!Z0KR5=L&Gyz5;n_;J4leZ6{7 z)CYASZdOvsTqv^aq-`dHSXW<8v{`N!G>n%MKzCy|L*4?Co-g-kZxGAMwyBn6nmCEH zgxzI@M|afsuN-SIOs@{u!SwC*Q}rMZ=ZEc0wI(y1=g z(_jBBTLQ0;U5r>TP@&-t%qOKo+1DYDP7&bSqCb+TO2!Cb_xpLHe654~m;MHz0Ew%{ z2{w770NEZ{evs}sNSz^;&S=b+A8L$wROdmcYu;IvSC^QS_N;LiMF>2TDI$JvqY=I) zYfH!|T`PK$1Pk^4Y@Mm5i4)D(ttD;5fnM`07W}-se5KVTJYS@07Uxvm5XtM?`$Fc< zYS$^I);+nYTv9`1r3Ikgv({0ckFMCCJ(1#4(GajC|4h*R4usZ#oWCFOO21{aiW~pj zHZ#p<*o^L`^?fXC_B0|-8Fm;PHkN*nlHbepou^Jdx_HJ33B`gLl22C~l&9_O=Gyy-Qz3mr{~ zag~^d{9W&D+;)Qh&G2z$3b|IVBFFMfOO%D>)opW1kxhFz@dRD_>a}u0mZL%V0}Tb8 zFU1jx{uG`HTx0(A?jt^#%zWthAI)G?IMNK(JwYUfLty%-sEe<1-abAJvK6ze(qkMb z4e9zAC1+t_LJXSc)UF3B_-8B2+?2RCTy%UK(jDHt+82>+P6F*i9%hh;wx~-6f^8n` zNpD-}m6K*IFP>$oR&g-L#0=cMvmWAc0wN|as3O0jglECx0me0=F1QWeP&fAKqn zl<~5q1vU$XaiMbBpW0Fiwr3D+0{`A?sPe=6HIFw$pAxQ=$``ti#ikhRW75$Fw%9yn437sewG~b8` zY1WuGVc$eNW4xaDa9Jj(%5|jblX;ri4h)N6>O}W1% zh~lL^q;2L3W9DbdsENxE_T*4{rJ>lqmI}uQFZ)z3>#H7EtjjE%rRES{KSl94 zBHpN>~T#w2G)PFn+648CMlDzYC zla>>AW`*-o81ps$P)+I|Vu=)@V4$a`D7#xbYy~`^zd}n0R2Yu{A6$0V{e&HhOr`7_ zCUCU+U}aSk0!ifZ*aO@2$@EU0VY_KmBPW)=$arA3nw#Aped14PT+@?6g}SJ%tR8KP1GL6zRlSN$vU zVYUfjRioONN{<&O7sE}pE^nIW(3*CZQ!|46rk*tLI;9kSr&wZsC^8$pZ12s7-Sj5DHz-Oat&PUks(Y%1uk2cAE>3L_AZpHfNKAV anW%&>Md>FCHrkgQV7z~KUf5yU|L`BPd^IZo literal 0 HcmV?d00001 diff --git a/webpack.config.js b/webpack.config.js index b58666d4..a9586f15 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -60,7 +60,13 @@ module.exports = [ libraryTarget: 'commonjs2', path: path.resolve(__dirname, 'dist/node'), filename: '[name].js' - } + }, + plugins: base.plugins.concat([ + new CopyWebpackPlugin([{ + from: './src/extensions/scratch3_music/assets', + to: 'assets/scratch3_music' + }]) + ]) }), // Playground defaultsDeep({}, base, { From 26d4a3a069253dbadaedf6bc82961255242814c2 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 13 Nov 2017 16:29:38 -0500 Subject: [PATCH 580/721] Refactoring representation of variable type. --- src/engine/target.js | 4 ++-- src/engine/variable.js | 22 ++++++++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/engine/target.js b/src/engine/target.js index ebe7d495..b67b42a5 100644 --- a/src/engine/target.js +++ b/src/engine/target.js @@ -124,7 +124,7 @@ class Target extends EventEmitter { const list = this.lookupVariableById(id); if (list) return list; // No variable with this name exists - create it locally. - const newList = new Variable(id, name, 'list', false); + const newList = new Variable(id, name, Variable.LIST_TYPE, false); this.variables[id] = newList; return newList; } @@ -134,7 +134,7 @@ class Target extends EventEmitter { * dictionary of variables. * @param {string} id Id of variable * @param {string} name Name of variable. - * @param {string} type Type of variable, one of string, number, list + * @param {string} type Type of variable, '' or 'list' */ createVariable (id, name, type) { if (!this.variables.hasOwnProperty(id)) { diff --git a/src/engine/variable.js b/src/engine/variable.js index 55617894..a44324f6 100644 --- a/src/engine/variable.js +++ b/src/engine/variable.js @@ -19,18 +19,36 @@ class Variable { this.type = type; this.isCloud = isCloud; switch (this.type) { - case '': + case Variable.SCALAR_TYPE: this.value = 0; break; - case 'list': + case Variable.LIST_TYPE: this.value = []; break; + default: + throw new Error(`Invalid variable type: ${this.type}`); } } toXML () { return `${this.name}`; } + + /** + * Type representation for scalar variables. + * @const {string} + */ + static get SCALAR_TYPE () { + return ''; + } + + /** + * Type representation for list variables. + * @const {string} + */ + static get LIST_TYPE () { + return 'list'; + } } module.exports = Variable; From 3f444138db9e8c4ed32804d5ca4ceb6c1a57877b Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 13 Nov 2017 16:30:47 -0500 Subject: [PATCH 581/721] Updating tests for refactoring lists. Updating demo fixture -- now we have IDs and variable types for variables. --- test/fixtures/demo.json | 2 +- test/unit/engine_target.js | 60 +++++++++++++++++++++++++++++++------- 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/test/fixtures/demo.json b/test/fixtures/demo.json index d54626da..1467a463 100644 --- a/test/fixtures/demo.json +++ b/test/fixtures/demo.json @@ -1 +1 @@ -{"targets":[{"id":"poXv!.~0@*~j*F(Om/X5","name":"Stage","isStage":true,"x":0,"y":0,"size":100,"direction":90,"draggable":false,"currentCostume":10,"costume":{"name":"backdrop2","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"skinId":7,"assetId":"7da4181ee167de7b3f5d1a91880277ff","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},"costumeCount":11,"visible":true,"rotationStyle":"all around","blocks":{"O7D.%E~TH^ULpAuHM8)@":{"id":"O7D.%E~TH^ULpAuHM8)@","opcode":"event_whenflagclicked","inputs":{},"fields":{},"next":"78+=E[P7b8!mBXMwQF`@","shadow":false,"x":138,"y":356.40000000000003,"topLevel":true,"parent":null},"78+=E[P7b8!mBXMwQF`@":{"id":"78+=E[P7b8!mBXMwQF`@","opcode":"control_forever","inputs":{"SUBSTACK":{"name":"SUBSTACK","block":"nlnk{2XqtVI*Qg,admbW","shadow":null}},"fields":{},"next":null,"shadow":false,"parent":"O7D.%E~TH^ULpAuHM8)@"},"nlnk{2XqtVI*Qg,admbW":{"id":"nlnk{2XqtVI*Qg,admbW","opcode":"looks_changeeffectby","inputs":{"EFFECT":{"name":"EFFECT","block":"*vh;qV87Q}5IP@sW=)wD","shadow":"*vh;qV87Q}5IP@sW=)wD"},"CHANGE":{"name":"CHANGE","block":"woYo[[v=PD(`R;qW{PZ%","shadow":"woYo[[v=PD(`R;qW{PZ%"}},"fields":{},"next":null,"shadow":false,"parent":"78+=E[P7b8!mBXMwQF`@"},"*vh;qV87Q}5IP@sW=)wD":{"id":"*vh;qV87Q}5IP@sW=)wD","opcode":"looks_effectmenu","inputs":{},"fields":{"EFFECT":{"name":"EFFECT","value":"color"}},"next":null,"topLevel":false,"parent":"nlnk{2XqtVI*Qg,admbW","shadow":true},"woYo[[v=PD(`R;qW{PZ%":{"id":"woYo[[v=PD(`R;qW{PZ%","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"nlnk{2XqtVI*Qg,admbW","shadow":true}},"variables":{"vy":{"name":"vy","value":-3.5165000000000006,"isCloud":false},"g":{"name":"g","value":"-.5","isCloud":false},"vx":{"name":"vx","value":1.4580000000000002,"isCloud":false},"i":{"name":"i","value":3,"isCloud":false},"d1":{"name":"d1","value":0,"isCloud":false},"x":{"name":"x","value":233.9173278872492,"isCloud":false},"acceleration":{"name":"acceleration","value":"-3","isCloud":false}},"lists":{},"costumes":[{"name":"blue sky2","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"skinId":0,"assetId":"7623e679b2baa2e7d48808614820844f","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},{"name":"woods","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"skinId":3,"assetId":"1e0f7a4c932423b13250b5cb44928109","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},{"name":"party","bitmapResolution":1,"rotationCenterX":251,"rotationCenterY":189,"skinId":2,"assetId":"108160d0e44d1c340182e31c9dc0758a","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},{"name":"boardwalk","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":6,"assetId":"de0e54cd11551566f044e7e6bc588b2c","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"blue sky3","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":179,"skinId":1,"assetId":"2024d59c1980e667e8f656134796e2c1","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},{"name":"underwater1","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":5,"assetId":"f339c6f31b11ea71d0fb8d607cec392e","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"underwater2","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":11,"assetId":"1517c21786d2d0edc2f3037408d850bd","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"stars","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":4,"assetId":"e87fed9c2a968dbeae8c94617e600e8c","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"parking-ramp","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":9,"assetId":"a7832479977c166ca0057f2a99a73305","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"backdrop1","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":10,"assetId":"f67dc7de38bac6fbb0ab68e46352521d","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"backdrop2","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"skinId":7,"assetId":"7da4181ee167de7b3f5d1a91880277ff","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}}],"sounds":[{"name":"pop","format":"","rate":11025,"sampleCount":258,"soundID":-1,"md5":"83a9787d4cb6f3b7632b4ddfebf74367.wav","data":null,"assetId":"83a9787d4cb6f3b7632b4ddfebf74367","assetType":{"contentType":"audio/x-wav","name":"Sound","runtimeFormat":"wav"}}]},{"id":"9!d8G^[6i]k*:l[W%4;l","name":"Earth","isStage":false,"x":-10,"y":10,"size":100,"direction":90,"draggable":false,"currentCostume":0,"costume":{"name":"earth","bitmapResolution":1,"rotationCenterX":72,"rotationCenterY":72,"skinId":8,"assetId":"814197522984a302972998b1a7f92d91","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},"costumeCount":1,"visible":true,"rotationStyle":"all around","blocks":{},"variables":{},"lists":{},"costumes":[{"name":"earth","bitmapResolution":1,"rotationCenterX":72,"rotationCenterY":72,"skinId":8,"assetId":"814197522984a302972998b1a7f92d91","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}}],"sounds":[{"name":"pop","format":"","rate":11025,"sampleCount":258,"soundID":-1,"md5":"83a9787d4cb6f3b7632b4ddfebf74367.wav","data":null,"assetId":"83a9787d4cb6f3b7632b4ddfebf74367","assetType":{"contentType":"audio/x-wav","name":"Sound","runtimeFormat":"wav"}}]}],"meta":{"semver":"3.0.0","vm":"0.1.0","agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"}} +{"targets":[{"id":"m#69^p_G_]:NI,ZR#?iy","name":"Stage","isStage":true,"x":0,"y":0,"size":100,"direction":90,"draggable":false,"currentCostume":10,"costume":{"skinId":10,"name":"backdrop2","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"dataFormat":"svg","assetId":"7da4181ee167de7b3f5d1a91880277ff"},"costumeCount":11,"visible":true,"rotationStyle":"all around","blocks":{"O7D.%E~TH^ULpAuHM8)@":{"id":"O7D.%E~TH^ULpAuHM8)@","opcode":"event_whenflagclicked","inputs":{},"fields":{},"next":"78+=E[P7b8!mBXMwQF`@","shadow":false,"x":138,"y":356.40000000000003,"topLevel":true,"parent":null},"78+=E[P7b8!mBXMwQF`@":{"id":"78+=E[P7b8!mBXMwQF`@","opcode":"control_forever","inputs":{"SUBSTACK":{"name":"SUBSTACK","block":"nlnk{2XqtVI*Qg,admbW","shadow":null}},"fields":{},"next":null,"shadow":false,"parent":"O7D.%E~TH^ULpAuHM8)@"},"nlnk{2XqtVI*Qg,admbW":{"id":"nlnk{2XqtVI*Qg,admbW","opcode":"looks_changeeffectby","inputs":{"EFFECT":{"name":"EFFECT","block":"*vh;qV87Q}5IP@sW=)wD","shadow":"*vh;qV87Q}5IP@sW=)wD"},"CHANGE":{"name":"CHANGE","block":"woYo[[v=PD(`R;qW{PZ%","shadow":"woYo[[v=PD(`R;qW{PZ%"}},"fields":{},"next":null,"shadow":false,"parent":"78+=E[P7b8!mBXMwQF`@"},"*vh;qV87Q}5IP@sW=)wD":{"id":"*vh;qV87Q}5IP@sW=)wD","opcode":"looks_effectmenu","inputs":{},"fields":{"EFFECT":{"name":"EFFECT","value":"color"}},"next":null,"topLevel":false,"parent":"nlnk{2XqtVI*Qg,admbW","shadow":true},"woYo[[v=PD(`R;qW{PZ%":{"id":"woYo[[v=PD(`R;qW{PZ%","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"nlnk{2XqtVI*Qg,admbW","shadow":true}},"variables":{"6C~ysz%{q=IY/@VyR,}u":{"id":"6C~ysz%{q=IY/@VyR,}u","name":"vy","value":-3.5165000000000006,"type":""},"@M%UU5XBzcjofm,PEY-3":{"id":"@M%UU5XBzcjofm,PEY-3","name":"g","value":"-.5","type":""},"rNxv=Ia#{]Jvhmo{G6cf":{"id":"rNxv=Ia#{]Jvhmo{G6cf","name":"vx","value":1.4580000000000002,"type":""},"fc2rL3s1GcD4UyOOCaN(":{"id":"fc2rL3s1GcD4UyOOCaN(","name":"i","value":3, "type":""},"0A~.tga@}|%UW31FAhz5":{"id":"0A~.tga@}|%UW31FAhz5","name":"d1","value":0, "type":""},"^T|b;%@AzqwgHp},y98=":{"id":"^T|b;%@AzqwgHp},y98=","name":"x","value":233.9173278872492,"type":""},"6m`MGC(+0!,Mwj8xE2OB":{"id":"6m`MGC(+0!,Mwj8xE2OB","name":"acceleration","value":"-3","type":""}},"lists":{},"costumes":[{"skinId":9,"name":"blue sky2","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"dataFormat":"svg","assetId":"7623e679b2baa2e7d48808614820844f"},{"skinId":13,"name":"woods","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"dataFormat":"svg","assetId":"1e0f7a4c932423b13250b5cb44928109"},{"skinId":5,"name":"party","bitmapResolution":1,"rotationCenterX":251,"rotationCenterY":189,"dataFormat":"svg","assetId":"108160d0e44d1c340182e31c9dc0758a"},{"skinId":3,"name":"boardwalk","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"dataFormat":"png","assetId":"de0e54cd11551566f044e7e6bc588b2c"},{"skinId":6,"name":"blue sky3","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":179,"dataFormat":"svg","assetId":"2024d59c1980e667e8f656134796e2c1"},{"skinId":14,"name":"underwater1","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"dataFormat":"png","assetId":"f339c6f31b11ea71d0fb8d607cec392e"},{"skinId":7,"name":"underwater2","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"dataFormat":"png","assetId":"1517c21786d2d0edc2f3037408d850bd"},{"skinId":8,"name":"stars","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"dataFormat":"png","assetId":"e87fed9c2a968dbeae8c94617e600e8c"},{"skinId":11,"name":"parking-ramp","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"dataFormat":"png","assetId":"a7832479977c166ca0057f2a99a73305"},{"skinId":12,"name":"backdrop1","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"dataFormat":"png","assetId":"f67dc7de38bac6fbb0ab68e46352521d"},{"skinId":10,"name":"backdrop2","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"dataFormat":"svg","assetId":"7da4181ee167de7b3f5d1a91880277ff"}],"sounds":[{"format":"","rate":11025,"sampleCount":258,"soundID":-1,"name":"pop","md5":"83a9787d4cb6f3b7632b4ddfebf74367.wav","data":null,"dataFormat":"wav","assetId":"83a9787d4cb6f3b7632b4ddfebf74367","soundId":"yGa~fKV|[-9r)^[i+xMU"}]},{"id":"./Q-Mm#v_Ifl9@[}Ye^8","name":"Earth","isStage":false,"x":-10,"y":10,"size":100,"direction":90,"draggable":false,"currentCostume":0,"costume":{"skinId":4,"name":"earth","bitmapResolution":1,"rotationCenterX":72,"rotationCenterY":72,"dataFormat":"svg","assetId":"814197522984a302972998b1a7f92d91"},"costumeCount":1,"visible":true,"rotationStyle":"all around","blocks":{},"variables":{},"lists":{},"costumes":[{"skinId":4,"name":"earth","bitmapResolution":1,"rotationCenterX":72,"rotationCenterY":72,"dataFormat":"svg","assetId":"814197522984a302972998b1a7f92d91"}],"sounds":[{"format":"","rate":11025,"sampleCount":258,"soundID":-1,"name":"pop","md5":"83a9787d4cb6f3b7632b4ddfebf74367.wav","data":null,"dataFormat":"wav","assetId":"83a9787d4cb6f3b7632b4ddfebf74367","soundId":"MRZ+h9~E:8O(Fk,.zDVC"}]}],"meta":{"semver":"3.0.0","vm":"0.1.0-prerelease.1510327661-prerelease.1510327675","agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36"}} diff --git a/test/unit/engine_target.js b/test/unit/engine_target.js index db5b5e76..70bf46e9 100644 --- a/test/unit/engine_target.js +++ b/test/unit/engine_target.js @@ -1,5 +1,6 @@ const test = require('tap').test; const Target = require('../../src/engine/target'); +const Variable = require('../../src/engine/variable'); test('spec', t => { const target = new Target(); @@ -23,14 +24,14 @@ test('spec', t => { // Create Variable tests. test('createVariable', t => { const target = new Target(); - target.createVariable('foo', 'bar', ''); + target.createVariable('foo', 'bar', Variable.SCALAR_TYPE); const variables = target.variables; t.equal(Object.keys(variables).length, 1); const variable = variables[Object.keys(variables)[0]]; t.equal(variable.id, 'foo'); t.equal(variable.name, 'bar'); - t.equal(variable.type, ''); + t.equal(variable.type, Variable.SCALAR_TYPE); t.equal(variable.value, 0); t.equal(variable.isCloud, false); @@ -40,8 +41,8 @@ test('createVariable', t => { // Create Same Variable twice. test('createVariable2', t => { const target = new Target(); - target.createVariable('foo', 'bar', ''); - target.createVariable('foo', 'bar', ''); + target.createVariable('foo', 'bar', Variable.SCALAR_TYPE); + target.createVariable('foo', 'bar', Variable.SCALAR_TYPE); const variables = target.variables; t.equal(Object.keys(variables).length, 1); @@ -50,16 +51,16 @@ test('createVariable2', t => { }); // Create a list -test('createVariable creates a list', t => { +test('createListVariable creates a list', t => { const target = new Target(); - target.createVariable('foo', 'bar', 'list'); + target.createVariable('foo', 'bar', Variable.LIST_TYPE); const variables = target.variables; t.equal(Object.keys(variables).length, 1); const variable = variables[Object.keys(variables)[0]]; t.equal(variable.id, 'foo'); t.equal(variable.name, 'bar'); - t.equal(variable.type, 'list'); + t.equal(variable.type, Variable.LIST_TYPE); t.assert(variable.value instanceof Array, true); t.equal(variable.value.length, 0); t.equal(variable.isCloud, false); @@ -67,10 +68,20 @@ test('createVariable creates a list', t => { t.end(); }); +test('createVariable throws when given invalid type', t => { + const target = new Target(); + t.throws( + (() => target.createVariable('foo', 'bar', 'baz')), + new Error('Invalid variable type: baz') + ); + + t.end(); +}); + // Rename Variable tests. test('renameVariable', t => { const target = new Target(); - target.createVariable('foo', 'bar', ''); + target.createVariable('foo', 'bar', Variable.SCALAR_TYPE); target.renameVariable('foo', 'bar2'); const variables = target.variables; @@ -99,7 +110,7 @@ test('renameVariable2', t => { // Expect no change. test('renameVariable3', t => { const target = new Target(); - target.createVariable('foo1', 'foo', ''); + target.createVariable('foo1', 'foo', Variable.SCALAR_TYPE); target.renameVariable('foo', 'bar2'); const variables = target.variables; @@ -114,7 +125,7 @@ test('renameVariable3', t => { // Delete Variable tests. test('deleteVariable', t => { const target = new Target(); - target.createVariable('foo', 'bar', ''); + target.createVariable('foo', 'bar', Variable.SCALAR_TYPE); target.deleteVariable('foo'); const variables = target.variables; @@ -133,3 +144,32 @@ test('deleteVariable2', t => { t.end(); }); + +test('lookupOrCreateList creates a list if var with given id does not exist', t => { + const target = new Target(); + const variables = target.variables; + + t.equal(Object.keys(variables).length, 0); + const listVar = target.lookupOrCreateList('foo', 'bar'); + t.equal(Object.keys(variables).length, 1); + t.equal(listVar.id, 'foo'); + t.equal(listVar.name, 'bar'); + + t.end(); +}); + +test('lookupOrCreateList returns list if one with given id exists', t => { + const target = new Target(); + const variables = target.variables; + + t.equal(Object.keys(variables).length, 0); + target.createVariable('foo', 'bar', Variable.LIST_TYPE); + t.equal(Object.keys(variables).length, 1); + + const listVar = target.lookupOrCreateList('foo', 'bar'); + t.equal(Object.keys(variables).length, 1); + t.equal(listVar.id, 'foo'); + t.equal(listVar.name, 'bar'); + + t.end(); +}); From 386045f033d074f5e61cfd95c75e827f9ad7eac1 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 13 Nov 2017 16:55:57 -0500 Subject: [PATCH 582/721] Some code cleanup. --- src/engine/target.js | 2 +- src/engine/variable.js | 2 +- src/serialization/sb2.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engine/target.js b/src/engine/target.js index b67b42a5..7b2f1d66 100644 --- a/src/engine/target.js +++ b/src/engine/target.js @@ -87,7 +87,7 @@ class Target extends EventEmitter { const variable = this.lookupVariableById(id); if (variable) return variable; // No variable with this name exists - create it locally. - const newVariable = new Variable(id, name, '', false); + const newVariable = new Variable(id, name, Variable.SCALAR_TYPE, false); this.variables[id] = newVariable; return newVariable; } diff --git a/src/engine/variable.js b/src/engine/variable.js index a44324f6..5896e593 100644 --- a/src/engine/variable.js +++ b/src/engine/variable.js @@ -9,7 +9,7 @@ class Variable { /** * @param {string} id Id of the variable. * @param {string} name Name of the variable. - * @param {(string|number)} type Type of the variable, one of "" or "list" + * @param {string} type Type of the variable, one of '' or 'list' * @param {boolean} isCloud Whether the variable is stored in the cloud. * @constructor */ diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index 0c6c3781..f7b7b30d 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -234,7 +234,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) { const newVariable = new Variable( getVariableId(variable.name), variable.name, - '', + Variable.SCALAR_TYPE, variable.isPersistent ); newVariable.value = variable.value; @@ -254,7 +254,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) { const newVariable = new Variable( getVariableId(list.listName), list.listName, - 'list', + Variable.LIST_TYPE, list.isPersistent ); newVariable.value = list.contents; From 9048a9b9e78f25fef129fc1663912a244729cb1d Mon Sep 17 00:00:00 2001 From: DD Date: Mon, 13 Nov 2017 16:28:50 -0500 Subject: [PATCH 583/721] Pipe through whether a monitor is sprite-specific --- src/engine/blocks.js | 14 +++++++++++--- src/engine/execute.js | 2 ++ src/engine/monitor-record.js | 2 ++ src/engine/runtime.js | 9 +++++---- src/util/uid.js | 3 ++- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 92a2d58e..7564d3ca 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -226,7 +226,8 @@ class Blocks { id: e.blockId, element: e.element, name: e.name, - value: e.newValue + value: e.newValue, + isSpriteSpecific: e.isSpriteSpecific }, optRuntime); break; case 'move': @@ -326,12 +327,14 @@ class Blocks { break; case 'checkbox': block.isMonitored = args.value; + block.targetId = args.isSpriteSpecific ? this._getTargetIdFromBlockId(block.id) : null; if (optRuntime && wasMonitored && !block.isMonitored) { optRuntime.requestRemoveMonitor(block.id); } else if (optRuntime && !wasMonitored && block.isMonitored) { optRuntime.requestAddMonitor(MonitorRecord({ // @todo(vm#564) this will collide if multiple sprites use same block id: block.id, + spriteName: block.targetId ? optRuntime.getTargetById(block.targetId).getName() : null, opcode: block.opcode, params: this._getBlockParams(block), // @todo(vm#565) for numerical values with decimals, some countries use comma @@ -406,12 +409,17 @@ class Blocks { runAllMonitored (runtime) { Object.keys(this._blocks).forEach(blockId => { if (this.getBlock(blockId).isMonitored) { - // @todo handle specific targets (e.g. apple x position) - runtime.addMonitorScript(blockId); + const targetId = this.getBlock(blockId).targetId; + runtime.addMonitorScript(blockId, targetId ? runtime.getTargetById(targetId) : null); } }); } + _getTargetIdFromBlockId (blockId) { + // First word of block ID. See makeToolboxXML in scratch-gui + return blockId.split('_')[0]; + } + /** * Block management: delete blocks and their associated scripts. * @param {!object} e Blockly delete event to be processed. diff --git a/src/engine/execute.js b/src/engine/execute.js index df9b5964..fe1688e6 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -68,8 +68,10 @@ const handleReport = function ( sequencer.runtime.visualReport(currentBlockId, resolvedValue); } if (thread.updateMonitor) { + const targetId = sequencer.runtime.monitorBlocks.getBlock(currentBlockId).targetId; sequencer.runtime.requestUpdateMonitor(Map({ id: currentBlockId, + spriteName: targetId ? sequencer.runtime.getTargetById(targetId).getName() : null, value: String(resolvedValue) })); } diff --git a/src/engine/monitor-record.js b/src/engine/monitor-record.js index 76ed0848..40e67c5e 100644 --- a/src/engine/monitor-record.js +++ b/src/engine/monitor-record.js @@ -2,6 +2,8 @@ const {Record} = require('immutable'); const MonitorRecord = Record({ id: null, + /** Present only if the monitor is sprite-specific, such as x position */ + spriteName: null, opcode: null, value: null, params: null diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 56fceadf..06d068bc 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -838,7 +838,7 @@ class Runtime extends EventEmitter { /** * Enqueue a script that when finished will update the monitor for the block. * @param {!string} topBlockId ID of block that starts the script. - * @param {?string} optTarget target ID for target to run script on. If not supplied, uses editing target. + * @param {?string} optTarget target Target to run script on. If not supplied, uses editing target. */ addMonitorScript (topBlockId, optTarget) { if (!optTarget) optTarget = this._editingTarget; @@ -1260,7 +1260,7 @@ class Runtime extends EventEmitter { * @param {!MonitorRecord} monitor Monitor to add. */ requestAddMonitor (monitor) { - this._monitorState = this._monitorState.set(monitor.id, monitor); + this._monitorState = this._monitorState.set(monitor.get('id'), monitor); } /** @@ -1271,9 +1271,10 @@ class Runtime extends EventEmitter { * the old monitor will keep its old value. */ requestUpdateMonitor (monitor) { - if (this._monitorState.has(monitor.get('id'))) { + const id = monitor.get('id'); + if (this._monitorState.has(id)) { this._monitorState = - this._monitorState.set(monitor.get('id'), this._monitorState.get(monitor.get('id')).merge(monitor)); + this._monitorState.set(id, this._monitorState.get(id).merge(monitor)); } } diff --git a/src/util/uid.js b/src/util/uid.js index fd4c41e8..34e8eaf5 100644 --- a/src/util/uid.js +++ b/src/util/uid.js @@ -6,9 +6,10 @@ * Legal characters for the unique ID. * Should be all on a US keyboard. No XML special characters or control codes. * Removed $ due to issue 251. + * Removed _ which denotes word separation in XML. * @private */ -const soup_ = '!#%()*+,-./:;=?@[]^_`{|}~' + +const soup_ = '!#%()*+,-./:;=?@[]^`{|}~' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; /** From 9f1bcd25946e562faa1bf9559ed93288c08d808b Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 14 Nov 2017 14:30:44 -0500 Subject: [PATCH 584/721] Arrays with drum and instrument name and filename --- src/extensions/scratch3_music/index.js | 225 +++++++++++++++++++------ 1 file changed, 172 insertions(+), 53 deletions(-) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index ba136003..94d22e29 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -5,59 +5,6 @@ const Cast = require('../../util/cast'); const MathUtil = require('../../util/math-util'); const Timer = require('../../util/timer'); -/** - * An array of drum names, used in the play drum block. - * @type {string[]} - */ -const drumNames = [ - 'Snare Drum', - 'Bass Drum', - 'Side Stick', - 'Crash Cymbal', - 'Open Hi-Hat', - 'Closed Hi-Hat', - 'Tambourine', - 'Hand Clap', - 'Claves', - 'Wood Block', - 'Cowbell', - 'Triangle', - 'Bongo', - 'Conga', - 'Cabasa', - 'Guiro', - 'Vibraslap', - 'Open Cuica' -]; - -/** - * An array of instrument names, used in the set instrument block. - * @type {string[]} - */ -const instrumentNames = [ - 'Piano', - 'Electric Piano', - 'Organ', - 'Guitar', - 'Electric Guitar', - 'Bass', - 'Pizzicato', - 'Cello', - 'Trombone', - 'Clarinet', - 'Saxophone', - 'Flute', - 'Wooden Flute', - 'Bassoon', - 'Choir', - 'Vibraphone', - 'Music Box', - 'Steel Drum', - 'Marimba', - 'Synth Lead', - 'Synth Pad' -]; - /** * Class for the music-related blocks in Scratch 3.0 * @param {Runtime} runtime - the runtime instantiating this block package. @@ -99,6 +46,178 @@ class Scratch3MusicBlocks { menu.push(entry); } return menu; + /** + * An array of translatable drum names and corresponding audio file names. + * @type {array} + */ + get DRUM_INFO () { + return [ + { + name: '(1) Snare Drum', + fileName: '1-snare' + }, + { + name: '(2) Bass Drum', + fileName: '2-bass-drum' + }, + { + name: '(3) Side Stick', + fileName: '3-side-stick' + }, + { + name: '(4) Crash Cymbal', + fileName: '4-crash-cymbal' + }, + { + name: '(5) Open Hi-Hat', + fileName: '5-open-hi-hat' + }, + { + name: '(6) Closed Hi-Hat', + fileName: '6-closed-hi-hat' + }, + { + name: '(7) Tambourine', + fileName: '7-tambourine' + }, + { + name: '(8) Hand Clap', + fileName: '8-hand-clap' + }, + { + name: '(9) Claves', + fileName: '9-claves' + }, + { + name: '(10) Wood Block', + fileName: '10-wood-block' + }, + { + name: '(11) Cowbell', + fileName: '11-cowbell' + }, + { + name: '(12) Triangle', + fileName: '12-triangle' + }, + { + name: '(13) Bongo', + fileName: '13-bongo' + }, + { + name: '(14) Conga', + fileName: '14-conga' + }, + { + name: '(15) Cabasa', + fileName: '15-cabasa' + }, + { + name: '(16) Guiro', + fileName: '16-guiro' + }, + { + name: '(17) Vibraslap', + fileName: '17-vibraslap' + }, + { + name: '(18) Cuica', + fileName: '18-cuica' + } + ]; + } + + /** + * An array of translatable instrument names and corresponding audio file names. + * @type {array} + */ + get INSTRUMENT_INFO () { + return [ + { + name: '(1) Piano', + fileName: '1-piano' + }, + { + name: '(2) Electric Piano', + fileName: '2-electric-piano' + }, + { + name: '(3) Organ', + fileName: '3-organ' + }, + { + name: '(4) Guitar', + fileName: '4-guitar' + }, + { + name: '(5) Electric Guitar', + fileName: '5-electric-guitar' + }, + { + name: '(6) Bass', + fileName: '6-bass' + }, + { + name: '(7) Pizzicato', + fileName: '7-pizzicato' + }, + { + name: '(8) Cello', + fileName: '8-cello' + }, + { + name: '(9) Trombone', + fileName: '9-trombone' + }, + { + name: '(10) Clarinet', + fileName: '10-clarinet' + }, + { + name: '(11) Saxophone', + fileName: '11-saxophone' + }, + { + name: '(12) Flute', + fileName: '12-flute' + }, + { + name: '(13) Wooden Flute', + fileName: '13-wooden-flute' + }, + { + name: '(14) Bassoon', + fileName: '14-bassoon' + }, + { + name: '(15) Choir', + fileName: '15-choir' + }, + { + name: '(16) Vibraphone', + fileName: '16-vibraphone' + }, + { + name: '(17) Music Box', + fileName: '17-music-box' + }, + { + name: '(18) Steel Drum', + fileName: '18-steel-drum' + }, + { + name: '(19) Marimba', + fileName: '19-marimba' + }, + { + name: '(20) Synth Lead', + fileName: '20-synth-lead' + }, + { + name: '(21) Synth Pad', + fileName: '21-synth-pad' + } + ]; } /** From 91878adfe1b963579d54595c13fb73557e6c91a7 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 14 Nov 2017 14:32:14 -0500 Subject: [PATCH 585/721] Build menus using drum and instrument info arrays --- src/extensions/scratch3_music/index.js | 33 ++++++++++++-------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index 94d22e29..c5d44a9c 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -25,27 +25,24 @@ class Scratch3MusicBlocks { */ this.tempo = 60; - this.drumMenu = this._buildMenu(drumNames); - this.instrumentMenu = this._buildMenu(instrumentNames); } /** - * Build a menu using an array of strings. - * Used for creating the drum and instrument menus. - * @param {string[]} names - An array of names. - * @return {array} - An array of objects with text and value properties, for constructing a block menu. + * Create data for a menu in scratch-blocks format, consisting of an array of objects with text and + * value properties. The text is a translated string, and the value is one-indexed. + * @param {object[]} info - An array of info objects each having a name property. + * @return {array} - An array of objects with text and value properties. * @private */ - _buildMenu (names) { - const menu = []; - for (let i = 0; i < names.length; i++) { - const entry = {}; - const num = i + 1; // Menu numbers are one-indexed - entry.text = `(${num}) ${names[i]}`; - entry.value = String(num); - menu.push(entry); - } - return menu; + _buildMenu (info) { + return info.map((entry, index) => { + const obj = {}; + obj.text = entry.name; + obj.value = index + 1; + return obj; + }); + } + /** * An array of translatable drum names and corresponding audio file names. * @type {array} @@ -367,8 +364,8 @@ class Scratch3MusicBlocks { } ], menus: { - drums: this.drumMenu, - instruments: this.instrumentMenu + drums: this._buildMenu(this.DRUM_INFO), + instruments: this._buildMenu(this.INSTRUMENT_INFO) } }; } From c26198d050b7b24580af3bfa36ef7d797b3f8337 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 14 Nov 2017 14:33:14 -0500 Subject: [PATCH 586/721] Load drum sounds into an array of buffers --- src/extensions/scratch3_music/index.js | 43 ++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index c5d44a9c..ba42c167 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -25,6 +25,49 @@ class Scratch3MusicBlocks { */ this.tempo = 60; + /** + * An array of audio buffers, one for each drum sound. + * @type {Array} + * @private + */ + this._drumBuffers = []; + + this._loadAllDrumSounds(); + } + + /** + * Download and decode the full set of drum sounds, and store the audio buffers + * in the drum buffers array. + * @TODO: Also load the instrument sounds here (rename this fn), and use Promise.all + * to detect that all the assets have loaded in order to update the extension status + * indicator. + */ + _loadAllDrumSounds () { + const loadingPromises = []; + this.DRUM_INFO.forEach((drumInfo, index) => { + const promise = this._loadSound(drumInfo.fileName, index, this._drumBuffers); + loadingPromises.push(promise); + }); + Promise.all(loadingPromises).then(() => { + // done! + }); + } + + /** + * Download and decode a sound, and store the buffer in an array. + * @param {string} fileName - the audio file name. + * @param {number} index - the index at which to store the audio buffer. + * @param {array} bufferArray - the array of buffers in which to store it. + * @return {Promise} - a promise which will resolve once the sound has loaded. + */ + _loadSound (fileName, index, bufferArray) { + return this.runtime.storage.load(this.runtime.storage.AssetType.Sound, fileName, 'mp3') + .then(soundAsset => + this.runtime.audioEngine.audioContext.decodeAudioData(soundAsset.data.buffer) + ) + .then(buffer => { + bufferArray[index] = buffer; + }); } /** From 14e2ca8d9921c5095d1768ee6e123c04ba5201a8 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 14 Nov 2017 14:35:26 -0500 Subject: [PATCH 587/721] Round the drum num arg --- src/extensions/scratch3_music/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index ba42c167..e0b2ca26 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -423,6 +423,7 @@ class Scratch3MusicBlocks { playDrumForBeats (args, util) { if (this._stackTimerNeedsInit(util)) { let drum = Cast.toNumber(args.DRUM); + drum = Math.round(drum); drum -= 1; // drums are one-indexed if (typeof this.runtime.audioEngine === 'undefined') return; drum = MathUtil.wrapClamp(drum, 0, this.runtime.audioEngine.numDrums - 1); From 73dbc824110da1436f471bb54f8da29710bf06e3 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 14 Nov 2017 14:36:34 -0500 Subject: [PATCH 588/721] Get number of drums from drum info array --- src/extensions/scratch3_music/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index e0b2ca26..84bd4679 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -425,8 +425,7 @@ class Scratch3MusicBlocks { let drum = Cast.toNumber(args.DRUM); drum = Math.round(drum); drum -= 1; // drums are one-indexed - if (typeof this.runtime.audioEngine === 'undefined') return; - drum = MathUtil.wrapClamp(drum, 0, this.runtime.audioEngine.numDrums - 1); + drum = MathUtil.wrapClamp(drum, 0, this.DRUM_INFO.length - 1); let beats = Cast.toNumber(args.BEATS); beats = this._clampBeats(beats); if (util.target.audioPlayer !== null) { From b64152637e8918b20270636e7ec9198844d3f1de Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 14 Nov 2017 14:39:11 -0500 Subject: [PATCH 589/721] Play drums! --- src/extensions/scratch3_music/index.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index 84bd4679..874dbe4f 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -428,15 +428,22 @@ class Scratch3MusicBlocks { drum = MathUtil.wrapClamp(drum, 0, this.DRUM_INFO.length - 1); let beats = Cast.toNumber(args.BEATS); beats = this._clampBeats(beats); - if (util.target.audioPlayer !== null) { - util.target.audioPlayer.playDrumForBeats(drum, beats); - } + this._playDrumNum(util, drum); this._startStackTimer(util, this._beatsToSec(beats)); } else { this._checkStackTimer(util); } } + _playDrumNum (util, drumNum) { + if (util.target.audioPlayer === null) return; + const outputNode = util.target.audioPlayer.getInputNode(); + const bufferSource = this.runtime.audioEngine.audioContext.createBufferSource(); + bufferSource.buffer = this._drumBuffers[drumNum]; + bufferSource.connect(outputNode); + bufferSource.start(); + } + /** * Rest for some number of beats. * @param {object} args - the block arguments. From 7a65fe7d07f6bdbf8e075689fc411542222b0d9c Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 14 Nov 2017 14:39:50 -0500 Subject: [PATCH 590/721] Limit number of concurrent drum sounds --- src/extensions/scratch3_music/index.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index 874dbe4f..1ccc340f 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -25,6 +25,13 @@ class Scratch3MusicBlocks { */ this.tempo = 60; + /** + * The number of drum sounds currently being played simultaneously. + * @type {number} + * @private + */ + this._drumConcurrencyCounter = 0; + /** * An array of audio buffers, one for each drum sound. * @type {Array} @@ -302,6 +309,14 @@ class Scratch3MusicBlocks { return {min: 20, max: 500}; } + /** + * The maximum number of sounds to allow to play simultaneously. + * @type {number} + */ + static get CONCURRENCY_LIMIT () { + return 30; + } + /** * @param {Target} target - collect music state for this target. * @returns {MusicState} the mutable music state associated with that target. This will be created if necessary. @@ -437,11 +452,18 @@ class Scratch3MusicBlocks { _playDrumNum (util, drumNum) { if (util.target.audioPlayer === null) return; + if (this._drumConcurrencyCounter > Scratch3MusicBlocks.CONCURRENCY_LIMIT) { + return; + } const outputNode = util.target.audioPlayer.getInputNode(); const bufferSource = this.runtime.audioEngine.audioContext.createBufferSource(); bufferSource.buffer = this._drumBuffers[drumNum]; bufferSource.connect(outputNode); bufferSource.start(); + this._drumConcurrencyCounter++; + bufferSource.onended = () => { + this._drumConcurrencyCounter--; + }; } /** From 9665b3ed25379a6a86fae569f3344d24f8a60c3b Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 14 Nov 2017 14:40:27 -0500 Subject: [PATCH 591/721] Always yield on first run of a timed block --- src/extensions/scratch3_music/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index 1ccc340f..48482005 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -547,9 +547,7 @@ class Scratch3MusicBlocks { util.stackFrame.timer = new Timer(); util.stackFrame.timer.start(); util.stackFrame.duration = duration; - if (util.stackFrame.duration > 0) { - util.yield(); - } + util.yield(); } /** From a5b55a51287d345fc1aeaa635f398540182c57d3 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Thu, 9 Nov 2017 17:10:09 -0500 Subject: [PATCH 592/721] Add runtime Profiler Profile internal virtual machine performance down to which blocks take how long. --- src/engine/profiler.js | 311 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 src/engine/profiler.js diff --git a/src/engine/profiler.js b/src/engine/profiler.js new file mode 100644 index 00000000..1433f36f --- /dev/null +++ b/src/engine/profiler.js @@ -0,0 +1,311 @@ +/** + * @fileoverview + * A way to profile Scratch internal performance. Like what blocks run during a + * step? How much time do they take? How much time is spent inbetween blocks? + * + * Profiler aims for to spend as little time inside its functions while + * recording. For this it has a simple internal record structure that records a + * series of values for each START and STOP event in a single array. This lets + * all the values be pushed in one call for the array. This simplicity allows + * the contents of the start() and stop() calls to be inlined in areas that are + * called frequently enough to want even greater performance from Profiler so + * what is recorded better reflects on the profiled code and not Profiler + * itself. + */ + +/** + * The next id returned for a new profile'd function. + * @type {number} + */ +let nextId = 0; + +/** + * The mapping of names to ids. + * @const {Object.} + */ +const profilerNames = {}; + +/** + * The START event identifier in Profiler records. + * @const {number} + */ +const START = 0; + +/** + * The STOP event identifier in Profiler records. + * @const {number} + */ +const STOP = 1; + +/** + * The number of cells used in the records array by a START event. + * @const {number} + */ +const START_SIZE = 4; + +/** + * The number of cells used in the records array by a STOP event. + * @const {number} + */ +const STOP_SIZE = 2; + +/** + * Stored reference to Performance instance provided by the Browser. + * @const {Performance} + */ +const performance = typeof window === 'object' && window.performance; + + +/** + * Callback handle called by Profiler for each frame it decodes from its + * records. + * @callback FrameCallback + * @param {ProfilerFrame} frame + */ + +/** + * A set of information about a frame of execution that was recorded. + */ +class ProfilerFrame { + /** + * @param {number} depth Depth of the frame in the recorded stack. + */ + constructor (depth) { + /** + * The numeric id of a record symbol like Runtime._step or + * blockFunction. + * @type {number} + */ + this.id = -1; + + /** + * The amount of time spent inside the recorded frame and any deeper + * frames. + * @type {number} + */ + this.totalTime = 0; + + /** + * The amount of time spent only inside this record frame. Not + * including time in any deeper frames. + * @type {number} + */ + this.selfTime = 0; + + /** + * An arbitrary argument for the recorded frame. For example a block + * function might record its opcode as an argument. + * @type {*} + */ + this.arg = null; + + /** + * The depth of the recorded frame. This can help compare recursive + * funtions that are recorded. Each level of recursion with have a + * different depth value. + * @type {number} + */ + this.depth = depth; + } +} + +class Profiler { + /** + * @param {FrameCallback} onFrame a handle called for each recorded frame. + * The passed frame value may not be stored as it'll be updated with later + * frame information. Any information that is further stored by the handler + * should make copies or reduce the information. + */ + constructor (onFrame = function () {}) { + /** + * A series of START and STOP values followed by arguments. After + * recording is complete the full set of records is reported back by + * stepping through the series to connect the relative START and STOP + * information. + * @type {Array.<*>} + */ + this.records = []; + + /** + * A cache of ProfilerFrames to reuse when reporting the recorded + * frames in records. + * @type {Array.} + */ + this._stack = [new ProfilerFrame(0)]; + + /** + * A callback handle called with each decoded frame when reporting back + * all the recorded times. + * @type {FrameCallback} + */ + this.onFrame = onFrame; + + /** + * A reference to the START record id constant. + * @const {number} + */ + this.START = START; + + /** + * A reference to the STOP record id constant. + * @const {number} + */ + this.STOP = STOP; + } + + /** + * Start recording a frame of time for an id and optional argument. + * @param {number} id The id returned by idByName for a name symbol like + * Runtime._step. + * @param {?*} arg An arbitrary argument value to store with the frame. + */ + start (id, arg) { + this.records.push(START, id, arg, performance.now()); + } + + /** + * Stop the current frame. + */ + stop () { + this.records.push(STOP, performance.now()); + } + + /** + * Decode records and report all frames to `this.onFrame`. + */ + reportFrames () { + const stack = this._stack; + let depth = 1; + + // Step through the records and initialize Frame instances from the + // START and STOP events. START and STOP events are separated by events + // for deeper frames run by higher frames. Frames are stored on a stack + // and reinitialized for each START event. When a stop event is reach + // the Frame for the current depth has its final values stored and its + // passed to the current onFrame callback. This way Frames are "pushed" + // for each START event and "popped" for each STOP and handed to an + // outside handle to any desired reduction of the collected data. + for (let i = 0; i < this.records.length;) { + if (this.records[i] === START) { + if (depth >= stack.length) { + stack.push(new ProfilerFrame(depth)); + } + + // Store id, arg, totalTime, and initialize selfTime. + const frame = stack[depth++]; + frame.id = this.records[i + 1]; + frame.arg = this.records[i + 2]; + // totalTime is first set as the time recorded by this START + // event. Once the STOP event is reached the stored start time + // is subtracted from the recorded stop time. The resulting + // difference is the actual totalTime, and replaces the start + // time in frame.totalTime. + // + // totalTime is used this way as a convenient member to store a + // value between the two events without needing additional + // members on the Frame or in a shadow map. + frame.totalTime = this.records[i + 3]; + // selfTime is decremented until we reach the STOP event for + // this frame. totalTime will be added to it then to get the + // time difference. + frame.selfTime = 0; + + i += START_SIZE; + } else if (this.records[i] === STOP) { + const now = this.records[i + 1]; + + const frame = stack[--depth]; + // totalTime is the difference between the start event time + // stored in totalTime and the stop event time pulled from this + // record. + frame.totalTime = now - frame.totalTime; + // selfTime is the difference of this frame's totalTime and the + // sum of totalTime of deeper frames. + frame.selfTime += frame.totalTime; + + // Remove this frames totalTime from the parent's selfTime. + stack[depth - 1].selfTime -= frame.totalTime; + + this.onFrame(frame); + + i += STOP_SIZE; + } else { + this.records.length = 0; + throw new Error('Unable to decode Profiler records.'); + } + } + + this.records.length = 0; + } + + /** + * Lookup or create an id for a frame name. + * @param {string} name The name to return an id for. + * @return {number} The id for the passed name. + */ + idByName (name) { + return Profiler.idByName(name); + } + + /** + * Reverse lookup the name from a given frame id. + * @param {number} id The id to search for. + * @return {string} The name for the given id. + */ + nameById (id) { + return Profiler.nameById(id); + } + + /** + * Lookup or create an id for a frame name. + * @static + * @param {string} name The name to return an id for. + * @return {number} The id for the passed name. + */ + static idByName (name) { + if (typeof profilerNames[name] !== 'number') { + profilerNames[name] = nextId++; + } + return profilerNames[name]; + } + + /** + * Reverse lookup the name from a given frame id. + * @static + * @param {number} id The id to search for. + * @return {string} The name for the given id. + */ + static nameById (id) { + for (const name in profilerNames) { + if (profilerNames[name] === id) { + return name; + } + } + return null; + } + + /** + * Profiler is only available on platforms with the Performance API. + * @return {boolean} Can the Profiler run in this browser? + */ + static available () { + return ( + typeof window === 'object' && + typeof window.performance !== 'undefined'); + } +} + + +/** + * A reference to the START record id constant. + * @const {number} + */ +Profiler.START = START; + +/** + * A reference to the STOP record id constant. + * @const {number} + */ +Profiler.STOP = STOP; + +module.exports = Profiler; From 516d4f6f30daeb4aabfc378ecb225d2610f914f6 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Thu, 9 Nov 2017 17:27:49 -0500 Subject: [PATCH 593/721] Add Profiler events to Runtime, Sequencer and execute --- src/engine/execute.js | 28 ++++++++++++++++ src/engine/runtime.js | 54 +++++++++++++++++++++++++++++++ src/engine/sequencer.js | 72 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) diff --git a/src/engine/execute.js b/src/engine/execute.js index df9b5964..07541c12 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -9,6 +9,18 @@ const {Map} = require('immutable'); */ const blockUtility = new BlockUtility(); +/** + * Profiler frame name for block functions. + * @const {string} + */ +const blockFunctionProfilerFrame = 'blockFunction'; + +/** + * Profiler frame ID for 'blockFunction'. + * @type {number} + */ +let blockFunctionProfilerId = -1; + /** * Utility function to determine if a value is a Promise. * @param {*} value Value to check for a Promise. @@ -207,7 +219,23 @@ const execute = function (sequencer, thread) { let primitiveReportedValue = null; blockUtility.sequencer = sequencer; blockUtility.thread = thread; + if (runtime.profiler !== null) { + if (blockFunctionProfilerId === -1) { + blockFunctionProfilerId = runtime.profiler.idByName(blockFunctionProfilerFrame); + } + // The method commented below has its code inlined underneath to reduce + // the bias recorded for the profiler's calls in this time sensitive + // execute function. + // + // runtime.profiler.start(blockFunctionProfilerId, opcode); + runtime.profiler.records.push( + runtime.profiler.START, blockFunctionProfilerId, opcode, performance.now()); + } primitiveReportedValue = blockFunction(argValues, blockUtility); + if (runtime.profiler !== null) { + // runtime.profiler.stop(blockFunctionProfilerId); + runtime.profiler.records.push(runtime.profiler.STOP, performance.now()); + } if (typeof primitiveReportedValue === 'undefined') { // No value reported - potentially a command block. diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 56fceadf..3ba17371 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -79,6 +79,24 @@ const ScratchBlocksConstants = { OUTPUT_SHAPE_SQUARE: 3 }; +/** + * Numeric ID for Runtime._step in Profiler instances. + * @type {number} + */ +let stepProfilerId = -1; + +/** + * Numeric ID for Sequencer.stepThreads in Profiler instances. + * @type {number} + */ +let stepThreadsProfilerId = -1; + +/** + * Numeric ID for RenderWebGL.draw in Profiler instances. + * @type {number} + */ +let rendererDrawProfilerId = -1; + /** * Manages targets, scripts, and the sequencer. * @constructor @@ -234,6 +252,13 @@ class Runtime extends EventEmitter { keyboard: new Keyboard(this), mouse: new Mouse(this) }; + + /** + * A runtime profiler that records timed events for later playback to + * diagnose Scratch performance. + * @type {Profiler} + */ + this.profiler = null; } /** @@ -1055,6 +1080,12 @@ class Runtime extends EventEmitter { * inactive threads after each iteration. */ _step () { + if (this.profiler !== null) { + if (stepProfilerId === -1) { + stepProfilerId = this.profiler.idByName('Runtime._step'); + } + this.profiler.start(stepProfilerId); + } // Find all edge-activated hats, and add them to threads to be evaluated. for (const hatType in this._hats) { if (!this._hats.hasOwnProperty(hatType)) continue; @@ -1065,7 +1096,16 @@ class Runtime extends EventEmitter { } this.redrawRequested = false; this._pushMonitors(); + if (this.profiler !== null) { + if (stepThreadsProfilerId === -1) { + stepThreadsProfilerId = this.profiler.idByName('Sequencer.stepThreads'); + } + this.profiler.start(stepThreadsProfilerId); + } const doneThreads = this.sequencer.stepThreads(); + if (this.profiler !== null) { + this.profiler.stop(); + } this._updateGlows(doneThreads); // Add done threads so that even if a thread finishes within 1 frame, the green // flag will still indicate that a script ran. @@ -1074,7 +1114,16 @@ class Runtime extends EventEmitter { this._getMonitorThreadCount([...this.threads, ...doneThreads])); if (this.renderer) { // @todo: Only render when this.redrawRequested or clones rendered. + if (this.profiler !== null) { + if (rendererDrawProfilerId === -1) { + rendererDrawProfilerId = this.profiler.idByName('RenderWebGL.draw'); + } + this.profiler.start(rendererDrawProfilerId); + } this.renderer.draw(); + if (this.profiler !== null) { + this.profiler.stop(); + } } if (this._refreshTargets) { @@ -1086,6 +1135,11 @@ class Runtime extends EventEmitter { this.emit(Runtime.MONITORS_UPDATE, this._monitorState); this._prevMonitorState = this._monitorState; } + + if (this.profiler !== null) { + this.profiler.stop(); + this.profiler.reportFrames(); + } } /** diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js index f4edc966..0c796a6e 100644 --- a/src/engine/sequencer.js +++ b/src/engine/sequencer.js @@ -2,6 +2,42 @@ const Timer = require('../util/timer'); const Thread = require('./thread'); const execute = require('./execute.js'); +/** + * Profiler frame name for stepping a single thread. + * @const {string} + */ +const stepThreadProfilerFrame = 'Sequencer.stepThread'; + +/** + * Profiler frame name for the inner loop of stepThreads. + * @const {string} + */ +const stepThreadsInnerProfilerFrame = 'Sequencer.stepThreads#inner'; + +/** + * Profiler frame name for execute. + * @const {string} + */ +const executeProfilerFrame = 'execute'; + +/** + * Profiler frame ID for stepThreadProfilerFrame. + * @type {number} + */ +let stepThreadProfilerId = -1; + +/** + * Profiler frame ID for stepThreadsInnerProfilerFrame. + * @type {number} + */ +let stepThreadsInnerProfilerId = -1; + +/** + * Profiler frame ID for executeProfilerFrame. + * @type {number} + */ +let executeProfilerId = -1; + class Sequencer { constructor (runtime) { /** @@ -47,6 +83,13 @@ class Sequencer { numActiveThreads > 0 && this.timer.timeElapsed() < WORK_TIME && (this.runtime.turboMode || !this.runtime.redrawRequested)) { + if (this.runtime.profiler !== null) { + if (stepThreadsInnerProfilerId === -1) { + stepThreadsInnerProfilerId = this.runtime.profiler.idByName(stepThreadsInnerProfilerFrame); + } + this.runtime.profiler.start(stepThreadsInnerProfilerId); + } + numActiveThreads = 0; // Attempt to run each thread one time. for (let i = 0; i < this.runtime.threads.length; i++) { @@ -69,7 +112,16 @@ class Sequencer { if (activeThread.status === Thread.STATUS_RUNNING || activeThread.status === Thread.STATUS_YIELD) { // Normal-mode thread: step. + if (this.runtime.profiler !== null) { + if (stepThreadProfilerId === -1) { + stepThreadProfilerId = this.runtime.profiler.idByName(stepThreadProfilerFrame); + } + this.runtime.profiler.start(stepThreadProfilerId); + } this.stepThread(activeThread); + if (this.runtime.profiler !== null) { + this.runtime.profiler.stop(); + } activeThread.warpTimer = null; if (activeThread.isKilled) { i--; // if the thread is removed from the list (killed), do not increase index @@ -82,6 +134,10 @@ class Sequencer { // We successfully ticked once. Prevents running STATUS_YIELD_TICK // threads on the next tick. ranFirstTick = true; + + if (this.runtime.profiler !== null) { + this.runtime.profiler.stop(); + } } // Filter inactive threads from `this.runtime.threads`. numActiveThreads = 0; @@ -129,7 +185,23 @@ class Sequencer { // Execute the current block. // Save the current block ID to notice if we did control flow. currentBlockId = thread.peekStack(); + if (this.runtime.profiler !== null) { + if (executeProfilerId === -1) { + executeProfilerId = this.runtime.profiler.idByName(executeProfilerFrame); + } + // The method commented below has its code inlined underneath to + // reduce the bias recorded for the profiler's calls in this + // time sensitive stepThread method. + // + // this.runtime.profiler.start(executeProfilerId, null); + this.runtime.profiler.records.push( + this.runtime.profiler.START, executeProfilerId, null, performance.now()); + } execute(this, thread); + if (this.runtime.profiler !== null) { + // this.runtime.profiler.stop(); + this.runtime.profiler.records.push(this.runtime.profiler.STOP, performance.now()); + } thread.blockGlowInFrame = currentBlockId; // If the thread has yielded or is waiting, yield to other threads. if (thread.status === Thread.STATUS_YIELD) { From f73dae828e4720cf237043d3af9565099d75481d Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Thu, 9 Nov 2017 17:28:40 -0500 Subject: [PATCH 594/721] Add enableProfiling and disableProfiling to Runtime --- src/engine/runtime.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 3ba17371..39be44db 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -7,6 +7,7 @@ const Blocks = require('./blocks'); const BlockType = require('../extension-support/block-type'); const Sequencer = require('./sequencer'); const Thread = require('./thread'); +const Profiler = require('./profiler'); // Virtual I/O devices. const Clock = require('../io/clock'); @@ -1467,6 +1468,24 @@ class Runtime extends EventEmitter { this._step(); }, interval); } + + /** + * Turn on profiling. + * @param {Profiler/FrameCallback} onFrame A callback handle passed a + * profiling frame when the profiler reports its collected data. + */ + enableProfiling (onFrame) { + if (Profiler.available()) { + this.profiler = new Profiler(onFrame); + } + } + + /** + * Turn off profiling. + */ + disableProfiling () { + this.profiler = null; + } } /** From ff82699440226d09fcab109e3ac4a5d439a6400b Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Thu, 9 Nov 2017 18:33:15 -0500 Subject: [PATCH 595/721] Add Block % stats panel Enable profiler when Block % is visible and display percent of time block functions get to execute versus drawing and any overhead. --- src/playground/playground.js | 67 ++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/playground/playground.js b/src/playground/playground.js index 321afb15..995502d7 100644 --- a/src/playground/playground.js +++ b/src/playground/playground.js @@ -40,6 +40,72 @@ const getAssetUrl = function (asset) { return assetUrlParts.join(''); }; +const addProfilerPanels = function ( + stats, vm, panelName = 'BLK%', panelFg = '#fff', panelBg = '#111') { + + vm.runtime.enableProfiling(); + if (vm.runtime.profiler === null) { + // Profiler isn't available on the local system. + return; + } + + const blockPercentPanel = stats.addPanel(new window.Stats.Panel(panelName, panelFg, panelBg)); + + // Store the profiler reference for later. + const profiler = vm.runtime.profiler; + // Set profiler to null to disable profiling until later. + vm.runtime.profiler = null; + + const stepThreadsProfilerId = profiler.idByName('Sequencer.stepThreads'); + const blockFunctionProfilerId = profiler.idByName('blockFunction'); + + let blockFunctionTime = 0; + const stepValues = []; + let lastUpdate = Date.now(); + + // Collect time used by `blockFunction` calls in `execute` and add a column + // to the stats graph of the average for the last second of recordings. + profiler.onFrame = function ({id, totalTime}) { + if (id === stepThreadsProfilerId && totalTime > 0) { + // This frame wraps Sequencer.stepThreads. + + // Push the most recently summed blockFunctionTime. + stepValues.push(blockFunctionTime / totalTime * 100); + // Every second, average the pushed values and render that as a new + // column in the stats graph. + if (Date.now() - lastUpdate > 1000) { + lastUpdate = Date.now(); + const average = stepValues.reduce( + (a, b) => a + b, + 0) / stepValues.length; + blockPercentPanel.update(average, 100); + stepValues.length = 0; + } + blockFunctionTime = 0; + } else if (id === blockFunctionProfilerId) { + // This frame wraps around each blockFunction call. + blockFunctionTime += totalTime; + } + }; + + // Set the stats panel to not display by default. + blockPercentPanel.dom.style.display = 'none'; + + // When the parent of the stats graphs is clicked, check if the + // blockPercentPanel is visible. If it is visible, enable profiling by + // setting the runtime's profiler to the stored Profiler instance. If it is + // not visible, disable profiling by setting the profiler to null. + stats.dom.addEventListener('click', () => { + if (blockPercentPanel.dom.style.display === 'block') { + vm.runtime.profiler = profiler; + } else { + vm.runtime.profiler = null; + } + }); + + return blockPercentPanel; +}; + window.onload = function () { // Lots of global variables to make debugging easier // Instantiate the VM. @@ -99,6 +165,7 @@ window.onload = function () { const stats = new window.Stats(); document.getElementById('tab-renderexplorer').appendChild(stats.dom); stats.dom.style.position = 'relative'; + addProfilerPanels(stats, vm, 'BLK%', '#fff', '#111'); stats.begin(); // Playground data tabs. From 75ec76cd57f7c14fbbf0782f69fa08e04a4e3fb9 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 14 Nov 2017 14:58:16 -0500 Subject: [PATCH 596/721] Fix unit test --- src/extensions/scratch3_music/index.js | 2 ++ test/unit/blocks_music.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index 48482005..8e80dcaa 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -68,6 +68,8 @@ class Scratch3MusicBlocks { * @return {Promise} - a promise which will resolve once the sound has loaded. */ _loadSound (fileName, index, bufferArray) { + if (!this.runtime.storage) return; + if (!this.runtime.audioEngine) return; return this.runtime.storage.load(this.runtime.storage.AssetType.Sound, fileName, 'mp3') .then(soundAsset => this.runtime.audioEngine.audioContext.decodeAudioData(soundAsset.data.buffer) diff --git a/test/unit/blocks_music.js b/test/unit/blocks_music.js index ae097beb..66c95083 100644 --- a/test/unit/blocks_music.js +++ b/test/unit/blocks_music.js @@ -1,5 +1,5 @@ const test = require('tap').test; -const Music = require('../../src/blocks/scratch3_music'); +const Music = require('../../src/extensions/scratch3_music/index.js'); let playedDrum; let playedInstrument; const runtime = { From 01e9000c04b41db9fd0b5d8f87e5d670e1ef90bc Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 14 Nov 2017 15:00:20 -0500 Subject: [PATCH 597/721] JSDoc for _playDrumNum --- src/extensions/scratch3_music/index.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index 8e80dcaa..ab461441 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -452,6 +452,12 @@ class Scratch3MusicBlocks { } } + /** + * Play a drum sound using its 0-indexed number. + * @param {object} util - utility object provided by the runtime. + * @param {number} drumNum - the number of the drum to play. + * @private + */ _playDrumNum (util, drumNum) { if (util.target.audioPlayer === null) return; if (this._drumConcurrencyCounter > Scratch3MusicBlocks.CONCURRENCY_LIMIT) { From 757dccd565ecc3367e5f637910b4e48e91aae24b Mon Sep 17 00:00:00 2001 From: DD Date: Tue, 14 Nov 2017 18:25:54 -0500 Subject: [PATCH 598/721] Move is sprite specific verification into VM in a temporary way. Remove monitors when their sprites are deleted. --- src/engine/blocks.js | 1 + src/engine/execute.js | 4 ++++ src/engine/monitor-record.js | 2 ++ src/engine/runtime.js | 9 +++++++++ src/virtual-machine.js | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 52 insertions(+) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 7564d3ca..155a2e5e 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -334,6 +334,7 @@ class Blocks { optRuntime.requestAddMonitor(MonitorRecord({ // @todo(vm#564) this will collide if multiple sprites use same block id: block.id, + targetId: block.targetId, spriteName: block.targetId ? optRuntime.getTargetById(block.targetId).getName() : null, opcode: block.opcode, params: this._getBlockParams(block), diff --git a/src/engine/execute.js b/src/engine/execute.js index fe1688e6..bbacca95 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -69,6 +69,10 @@ const handleReport = function ( } if (thread.updateMonitor) { const targetId = sequencer.runtime.monitorBlocks.getBlock(currentBlockId).targetId; + if (targetId && !sequencer.runtime.getTargetById(targetId)) { + // Target no longer exists + return; + } sequencer.runtime.requestUpdateMonitor(Map({ id: currentBlockId, spriteName: targetId ? sequencer.runtime.getTargetById(targetId).getName() : null, diff --git a/src/engine/monitor-record.js b/src/engine/monitor-record.js index 40e67c5e..d42b878f 100644 --- a/src/engine/monitor-record.js +++ b/src/engine/monitor-record.js @@ -4,6 +4,8 @@ const MonitorRecord = Record({ id: null, /** Present only if the monitor is sprite-specific, such as x position */ spriteName: null, + /** Present only if the monitor is sprite-specific, such as x position */ + targetId: null, opcode: null, value: null, params: null diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 06d068bc..6d650668 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -1287,6 +1287,15 @@ class Runtime extends EventEmitter { this._monitorState = this._monitorState.delete(monitorId); } + /** + * Removes all monitors with the given target ID from the state. Does nothing if + * the monitor already does not exist in the state. + * @param {!string} targetId Remove all monitors with given target ID. + */ + requestRemoveMonitorByTargetId (targetId) { + this._monitorState = this._monitorState.filterNot(value => value.targetId === targetId); + } + /** * Get a target by its id. * @param {string} targetId Id of target to find. diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 6dfc7aca..175b5fd2 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -484,6 +484,7 @@ class VirtualMachine extends EventEmitter { if (!sprite) { throw new Error('No sprite associated with this target.'); } + this.runtime.requestRemoveMonitorByTargetId(targetId); const currentEditingTarget = this.editingTarget; for (let i = 0; i < sprite.clones.length; i++) { const clone = sprite.clones[i]; @@ -574,9 +575,44 @@ class VirtualMachine extends EventEmitter { * @param {!Blockly.Event} e Any Blockly event. */ monitorBlockListener (e) { + const tempMonitoredBlocks = [ + 'volume', + 'tempo', + 'answer', + 'loudness', + 'videoon', + 'timer', + 'of', + 'current', + 'username', + 'xposition', + 'yposition', + 'direction', + 'size', + 'backdropname', + 'costumeorder', + 'backdroporder' + ]; + const tempMonitoredPerSpriteBlocks = [ + 'xposition', + 'yposition', + 'direction', + 'size', + 'costumeorder' + ]; // Filter events by type, since monitor blocks only need to listen to these events. // Monitor blocks shouldn't be destroyed when flyout blocks are deleted. if (['create', 'change'].indexOf(e.type) !== -1) { + // TEMPORARY ---- + let blockType = e.blockId.split('_'); + blockType = blockType[blockType.length - 1]; + if (tempMonitoredBlocks.indexOf(blockType) === -1) { + return; + } + if (tempMonitoredPerSpriteBlocks.indexOf(blockType) !== -1) { + e.isSpriteSpecific = true; + } + // ----- this.runtime.monitorBlocks.blocklyListen(e, this.runtime); } } From fe86e38210e7487e2d51c5a41416fc4d8dee59ea Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 15 Nov 2017 09:17:20 -0500 Subject: [PATCH 599/721] Addressing PR review comments. --- src/engine/variable.js | 2 ++ src/serialization/sb2.js | 2 +- src/serialization/sb2_specmap.js | 32 +++++++++++++++++--------------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/engine/variable.js b/src/engine/variable.js index 5896e593..d75c0b82 100644 --- a/src/engine/variable.js +++ b/src/engine/variable.js @@ -36,6 +36,8 @@ class Variable { /** * Type representation for scalar variables. + * This is currently represented as '' + * for compatibility with blockly. * @const {string} */ static get SCALAR_TYPE () { diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index f7b7b30d..c6590fc8 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -255,7 +255,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) { getVariableId(list.listName), list.listName, Variable.LIST_TYPE, - list.isPersistent + false ); newVariable.value = list.contents; target.variables[newVariable.id] = newVariable; diff --git a/src/serialization/sb2_specmap.js b/src/serialization/sb2_specmap.js index c7de538e..7e9901e0 100644 --- a/src/serialization/sb2_specmap.js +++ b/src/serialization/sb2_specmap.js @@ -22,6 +22,8 @@ * Finally, I filled in the expected arguments as below. */ +const Variable = require('../engine/variable'); + /** * @typedef {object} SB2SpecMap_blockInfo * @property {string} opcode - the Scratch 3.0 block opcode. Use 'extensionID.opcode' for extension opcodes. @@ -1202,7 +1204,7 @@ const specMap = { { type: 'field', fieldName: 'VARIABLE', - variableType: '' + variableType: Variable.SCALAR_TYPE } ] }, @@ -1212,7 +1214,7 @@ const specMap = { { type: 'field', fieldName: 'VARIABLE', - variableType: '' + variableType: Variable.SCALAR_TYPE }, { type: 'input', @@ -1227,7 +1229,7 @@ const specMap = { { type: 'field', fieldName: 'VARIABLE', - variableType: '' + variableType: Variable.SCALAR_TYPE }, { type: 'input', @@ -1242,7 +1244,7 @@ const specMap = { { type: 'field', fieldName: 'VARIABLE', - variableType: '' + variableType: Variable.SCALAR_TYPE } ] }, @@ -1252,7 +1254,7 @@ const specMap = { { type: 'field', fieldName: 'VARIABLE', - variableType: '' + variableType: Variable.SCALAR_TYPE } ] }, @@ -1262,7 +1264,7 @@ const specMap = { { type: 'field', fieldName: 'LIST', - variableType: 'list' + variableType: Variable.LIST_TYPE } ] }, @@ -1277,7 +1279,7 @@ const specMap = { { type: 'field', fieldName: 'LIST', - variableType: 'list' + variableType: Variable.LIST_TYPE } ] }, @@ -1292,7 +1294,7 @@ const specMap = { { type: 'field', fieldName: 'LIST', - variableType: 'list' + variableType: Variable.LIST_TYPE } ] }, @@ -1312,7 +1314,7 @@ const specMap = { { type: 'field', fieldName: 'LIST', - variableType: 'list' + variableType: Variable.LIST_TYPE } ] }, @@ -1327,7 +1329,7 @@ const specMap = { { type: 'field', fieldName: 'LIST', - variableType: 'list' + variableType: Variable.LIST_TYPE }, { type: 'input', @@ -1347,7 +1349,7 @@ const specMap = { { type: 'field', fieldName: 'LIST', - variableType: 'list' + variableType: Variable.LIST_TYPE } ] }, @@ -1357,7 +1359,7 @@ const specMap = { { type: 'field', fieldName: 'LIST', - variableType: 'list' + variableType: Variable.LIST_TYPE } ] }, @@ -1367,7 +1369,7 @@ const specMap = { { type: 'field', fieldName: 'LIST', - variableType: 'list' + variableType: Variable.LIST_TYPE }, { type: 'input', @@ -1382,7 +1384,7 @@ const specMap = { { type: 'field', fieldName: 'LIST', - variableType: 'list' + variableType: Variable.LIST_TYPE } ] }, @@ -1392,7 +1394,7 @@ const specMap = { { type: 'field', fieldName: 'LIST', - variableType: 'list' + variableType: Variable.LIST_TYPE } ] }, From aa0064948a0a9435fb38281daa64efa6a94cd230 Mon Sep 17 00:00:00 2001 From: DD Date: Wed, 15 Nov 2017 11:34:20 -0500 Subject: [PATCH 600/721] Delete monitor block when variable is deleted --- src/engine/blocks.js | 20 +++++++++----------- src/engine/target.js | 1 + test/unit/engine_blocks.js | 12 +++--------- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 92a2d58e..11208bea 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -250,9 +250,7 @@ class Blocks { if (optRuntime && this._blocks[e.blockId].topLevel) { optRuntime.quietGlow(e.blockId); } - this.deleteBlock({ - id: e.blockId - }); + this.deleteBlock(e.blockId); break; case 'var_create': // New variables being created by the user are all global. @@ -414,37 +412,37 @@ class Blocks { /** * Block management: delete blocks and their associated scripts. - * @param {!object} e Blockly delete event to be processed. + * @param {!string} blockId Id of block to delete */ - deleteBlock (e) { + deleteBlock (blockId) { // @todo In runtime, stop threads running on this script. // Get block - const block = this._blocks[e.id]; + const block = this._blocks[blockId]; // Delete children if (block.next !== null) { - this.deleteBlock({id: block.next}); + this.deleteBlock(block.next); } // Delete inputs (including branches) for (const input in block.inputs) { // If it's null, the block in this input moved away. if (block.inputs[input].block !== null) { - this.deleteBlock({id: block.inputs[input].block}); + this.deleteBlock(block.inputs[input].block); } // Delete obscured shadow blocks. if (block.inputs[input].shadow !== null && block.inputs[input].shadow !== block.inputs[input].block) { - this.deleteBlock({id: block.inputs[input].shadow}); + this.deleteBlock(block.inputs[input].shadow); } } // Delete any script starting with this block. - this._deleteScript(e.id); + this._deleteScript(blockId); // Delete block itself. - delete this._blocks[e.id]; + delete this._blocks[blockId]; } // --------------------------------------------------------------------- diff --git a/src/engine/target.js b/src/engine/target.js index 2a6be81a..b94c221b 100644 --- a/src/engine/target.js +++ b/src/engine/target.js @@ -189,6 +189,7 @@ class Target extends EventEmitter { if (this.variables.hasOwnProperty(id)) { delete this.variables[id]; if (this.runtime) { + this.runtime.monitorBlocks.deleteBlock(id); this.runtime.requestRemoveMonitor(id); } } diff --git a/test/unit/engine_blocks.js b/test/unit/engine_blocks.js index 1055cda6..4353fe47 100644 --- a/test/unit/engine_blocks.js +++ b/test/unit/engine_blocks.js @@ -421,9 +421,7 @@ test('delete', t => { inputs: {}, topLevel: true }); - b.deleteBlock({ - id: 'foo' - }); + b.deleteBlock('foo'); t.type(b._blocks.foo, 'undefined'); t.equal(b._scripts.indexOf('foo'), -1); @@ -458,9 +456,7 @@ test('delete chain', t => { inputs: {}, topLevel: false }); - b.deleteBlock({ - id: 'foo' - }); + b.deleteBlock('foo'); t.type(b._blocks.foo, 'undefined'); t.type(b._blocks.foo2, 'undefined'); t.type(b._blocks.foo3, 'undefined'); @@ -531,9 +527,7 @@ test('delete inputs', t => { inputs: {}, topLevel: false }); - b.deleteBlock({ - id: 'foo' - }); + b.deleteBlock('foo'); t.type(b._blocks.foo, 'undefined'); t.type(b._blocks.foo2, 'undefined'); t.type(b._blocks.foo3, 'undefined'); From c8d5ba1d5e19271f6eff498c20f95973df34de6a Mon Sep 17 00:00:00 2001 From: DD Date: Wed, 15 Nov 2017 11:55:22 -0500 Subject: [PATCH 601/721] Delete block does nothing if the block doesnt exist --- src/engine/blocks.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 11208bea..fe6b1160 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -411,7 +411,8 @@ class Blocks { } /** - * Block management: delete blocks and their associated scripts. + * Block management: delete blocks and their associated scripts. Does nothing if a block + * with the given ID does not exist. * @param {!string} blockId Id of block to delete */ deleteBlock (blockId) { @@ -419,6 +420,10 @@ class Blocks { // Get block const block = this._blocks[blockId]; + if (!block) { + // No block with the given ID exists + return; + } // Delete children if (block.next !== null) { From ba529ad50b19b178431f6bd2379160957eeaca0d Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 15 Nov 2017 14:10:50 -0500 Subject: [PATCH 602/721] Update unit test --- test/unit/blocks_music.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/unit/blocks_music.js b/test/unit/blocks_music.js index 66c95083..b9e3c8af 100644 --- a/test/unit/blocks_music.js +++ b/test/unit/blocks_music.js @@ -4,7 +4,6 @@ let playedDrum; let playedInstrument; const runtime = { audioEngine: { - numDrums: 3, numInstruments: 3, instrumentPlayer: { loadInstrument: instrument => (playedInstrument = instrument) @@ -12,13 +11,14 @@ const runtime = { } }; const blocks = new Music(runtime); +blocks._playDrumNum = (util, drum) => (playedDrum = drum); + const util = { + stackFrame: Object.create(null), target: { - audioPlayer: { - playDrumForBeats: drum => (playedDrum = drum) - } + audioPlayer: null }, - stackFrame: Object.create(null) + yield: () => null }; test('playDrum uses 1-indexing and wrap clamps', t => { @@ -26,7 +26,7 @@ test('playDrum uses 1-indexing and wrap clamps', t => { blocks.playDrumForBeats(args, util); t.strictEqual(playedDrum, 0); - args = {DRUM: runtime.audioEngine.numDrums + 1}; + args = {DRUM: blocks.DRUM_INFO.length + 1}; blocks.playDrumForBeats(args, util); t.strictEqual(playedDrum, 0); From 0958db2618705512dfe9172fc206a5fee6e6edd2 Mon Sep 17 00:00:00 2001 From: DD Date: Wed, 15 Nov 2017 17:53:43 -0500 Subject: [PATCH 603/721] Move data to blocks files --- src/blocks/scratch3_looks.js | 9 +++++++++ src/blocks/scratch3_motion.js | 8 ++++++++ src/blocks/scratch3_sensing.js | 10 ++++++++++ src/blocks/scratch3_sound.js | 6 ++++++ src/engine/runtime.js | 26 ++++++++++++++++++++++++++ src/virtual-machine.js | 32 +++----------------------------- 6 files changed, 62 insertions(+), 29 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index 70238a78..a64f095b 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -242,6 +242,15 @@ class Scratch3LooksBlocks { }; } + getMonitored () { + return { + size: {isSpriteSpecific: true}, + costumeorder: {isSpriteSpecific: true}, + backdroporder: {}, + backdropname: {} + }; + } + say (args, util) { // @TODO in 2.0 calling say/think resets the right/left bias of the bubble this._updateBubble(util.target, 'say', String(args.MESSAGE)); diff --git a/src/blocks/scratch3_motion.js b/src/blocks/scratch3_motion.js index d040337c..b30138d3 100644 --- a/src/blocks/scratch3_motion.js +++ b/src/blocks/scratch3_motion.js @@ -38,6 +38,14 @@ class Scratch3MotionBlocks { }; } + getMonitored () { + return { + xposition: {isSpriteSpecific: true}, + yposition: {isSpriteSpecific: true}, + direction: {isSpriteSpecific: true} + }; + } + moveSteps (args, util) { const steps = Cast.toNumber(args.STEPS); const radians = MathUtil.degToRad(90 - util.target.direction); diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index 17dc87e9..d51dd5d3 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -49,6 +49,16 @@ class Scratch3SensingBlocks { }; } + getMonitored () { + return { + answer: {}, + loudness: {}, + timer: {}, + of: {}, + current: {} + }; + } + _onAnswer (answer) { this._answer = answer; const questionObj = this._questionList.shift(); diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 6f5fed97..d3f3f92a 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -103,6 +103,12 @@ class Scratch3SoundBlocks { }; } + getMonitored () { + return { + volume: {} + }; + } + playSound (args, util) { const index = this._getSoundIndex(args.SOUND_MENU, util); if (index >= 0) { diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 6d650668..d8fa8835 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -176,6 +176,13 @@ class Runtime extends EventEmitter { */ this._refreshTargets = false; + /** + * Map to look up all monitor block information by opcode. + * @type {object} + * @private + */ + this.monitorBlockInfo = {}; + /** * Ordered map of all monitors, which are MonitorReporter objects. */ @@ -225,6 +232,9 @@ class Runtime extends EventEmitter { // Register all given block packages. this._registerBlockPackages(); + // Populate monitorBlockInfo + this._registerMonitorInfo(); + // Register and initialize "IO devices", containers for processing // I/O related data. /** @type {Object.} */ @@ -451,6 +461,22 @@ class Runtime extends EventEmitter { this.emit(Runtime.EXTENSION_ADDED, categoryInfo.blocks.concat(categoryInfo.menus)); } + /** + * Populate this.monitorBlockInfo + */ + _registerMonitorInfo () { + for (const packageName in defaultBlockPackages) { + if (defaultBlockPackages.hasOwnProperty(packageName)) { + // @todo pass a different runtime depending on package privilege? + const packageObject = new (defaultBlockPackages[packageName])(this); + // Collect monitored from package. + if (packageObject.getMonitored) { + this.monitorBlockInfo = Object.assign({}, this.monitorBlockInfo, packageObject.getMonitored()); + } + } + } + } + /** * Build the scratch-blocks JSON for a menu. Note that scratch-blocks treats menus as a special kind of block. * @param {string} menuName - the name of the menu diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 175b5fd2..5c5361ba 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -575,44 +575,18 @@ class VirtualMachine extends EventEmitter { * @param {!Blockly.Event} e Any Blockly event. */ monitorBlockListener (e) { - const tempMonitoredBlocks = [ - 'volume', - 'tempo', - 'answer', - 'loudness', - 'videoon', - 'timer', - 'of', - 'current', - 'username', - 'xposition', - 'yposition', - 'direction', - 'size', - 'backdropname', - 'costumeorder', - 'backdroporder' - ]; - const tempMonitoredPerSpriteBlocks = [ - 'xposition', - 'yposition', - 'direction', - 'size', - 'costumeorder' - ]; // Filter events by type, since monitor blocks only need to listen to these events. // Monitor blocks shouldn't be destroyed when flyout blocks are deleted. if (['create', 'change'].indexOf(e.type) !== -1) { - // TEMPORARY ---- let blockType = e.blockId.split('_'); blockType = blockType[blockType.length - 1]; - if (tempMonitoredBlocks.indexOf(blockType) === -1) { + if (!this.runtime.monitorBlockInfo.hasOwnProperty(blockType)) { return; } - if (tempMonitoredPerSpriteBlocks.indexOf(blockType) !== -1) { + if (!this.runtime.monitorBlockInfo.hasOwnProperty(blockType) || + this.runtime.monitorBlockInfo[blockType].isSpriteSpecific) { e.isSpriteSpecific = true; } - // ----- this.runtime.monitorBlocks.blocklyListen(e, this.runtime); } } From 9864403bc977ae212b227ae55d1b384fa07949b6 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Tue, 7 Nov 2017 14:15:53 -0500 Subject: [PATCH 604/721] A thread in the runtime but with the DONE status is not active return false from isActiveThread if thread's status is done even when its contained in the runtime threads list. As it is done, the thread will not be run until either its replaced by a new copy of the thread or the thread is removed from the list and another is added at a later time. --- src/engine/runtime.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 39be44db..bd1e4a90 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -825,7 +825,11 @@ class Runtime extends EventEmitter { * @return {boolean} True if the thread is active/running. */ isActiveThread (thread) { - return this.threads.indexOf(thread) > -1; + return ( + ( + thread.stack.length > 0 && + thread.status !== Thread.STATUS_DONE) && + this.threads.indexOf(thread) > -1); } /** From 3d9255345986209c8b885b55b983bbb39224d9ea Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Tue, 7 Nov 2017 14:22:20 -0500 Subject: [PATCH 605/721] Return restarted threads in list from startHats Add restarted threads to the list of newThreads returned by startHats. --- src/engine/runtime.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index bd1e4a90..2f34cd8d 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -804,6 +804,7 @@ class Runtime extends EventEmitter { * This is used by `startHats` to and is necessary to ensure 2.0-like execution order. * Test project: https://scratch.mit.edu/projects/130183108/ * @param {!Thread} thread Thread object to restart. + * @return {Thread} The restarted thread. */ _restartThread (thread) { const newThread = new Thread(thread.topBlock); @@ -814,9 +815,10 @@ class Runtime extends EventEmitter { const i = this.threads.indexOf(thread); if (i > -1) { this.threads[i] = newThread; - } else { - this.threads.push(thread); + return newThread; } + this.threads.push(thread); + return thread; } /** @@ -976,7 +978,7 @@ class Runtime extends EventEmitter { if (instance.threads[i].topBlock === topBlockId && !instance.threads[i].stackClick && // stack click threads and hat threads can coexist instance.threads[i].target === target) { - instance._restartThread(instance.threads[i]); + newThreads.push(instance._restartThread(instance.threads[i])); return; } } From d173326290e592c79f006a97f36db5a62c441a26 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Thu, 16 Nov 2017 11:21:51 -0500 Subject: [PATCH 606/721] Add broadcastAndWait regression test --- test/unit/blocks_event.js | 94 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 test/unit/blocks_event.js diff --git a/test/unit/blocks_event.js b/test/unit/blocks_event.js new file mode 100644 index 00000000..2e62125b --- /dev/null +++ b/test/unit/blocks_event.js @@ -0,0 +1,94 @@ +const test = require('tap').test; + +const Blocks = require('../../src/engine/blocks'); +const BlockUtility = require('../../src/engine/block-utility'); +const Event = require('../../src/blocks/scratch3_event'); +const Runtime = require('../../src/engine/runtime'); +const Target = require('../../src/engine/target'); +const Thread = require('../../src/engine/thread'); + +test('#760 - broadcastAndWait', t => { + const broadcastAndWaitBlock = { + id: 'broadcastAndWaitBlock', + fields: { + BROADCAST_OPTION: { + id: 'BROADCAST_OPTION', + value: 'message' + } + }, + inputs: Object, + block: 'fakeBlock', + opcode: 'event_broadcastandwait', + next: null, + parent: null, + shadow: false, + topLevel: true, + x: 0, + y: 0 + }; + const receiveMessageBlock = { + id: 'receiveMessageBlock', + fields: { + BROADCAST_OPTION: { + id: 'BROADCAST_OPTION', + value: 'message' + } + }, + inputs: Object, + block: 'fakeBlock', + opcode: 'event_whenbroadcastreceived', + next: null, + parent: null, + shadow: false, + topLevel: true, + x: 0, + y: 0 + }; + + const rt = new Runtime(); + const e = new Event(rt); + const b = new Blocks(); + b.createBlock(broadcastAndWaitBlock); + b.createBlock(receiveMessageBlock); + const tgt = new Target(rt, b); + rt.targets.push(tgt); + + let th = rt._pushThread('broadcastAndWaitBlock', t); + const util = new BlockUtility(); + util.sequencer = rt.sequencer; + util.thread = th; + + // creates threads + e.broadcastAndWait({BROADCAST_OPTION: 'message'}, util); + t.strictEqual(rt.threads.length, 2); + t.strictEqual(rt.threads[1].topBlock, 'receiveMessageBlock'); + // yields when some thread is active + t.strictEqual(th.status, Thread.STATUS_YIELD); + th.status = Thread.STATUS_RUNNING; + e.broadcastAndWait({BROADCAST_OPTION: 'message'}, util); + t.strictEqual(th.status, Thread.STATUS_YIELD); + // does not yield once all threads are done + th.status = Thread.STATUS_RUNNING; + rt.threads[1].status = Thread.STATUS_DONE; + e.broadcastAndWait({BROADCAST_OPTION: 'message'}, util); + t.strictEqual(th.status, Thread.STATUS_RUNNING); + + // restarts done threads that are in runtime threads + th = rt._pushThread('broadcastAndWaitBlock', tgt); + util.thread = th; + e.broadcastAndWait({BROADCAST_OPTION: 'message'}, util); + t.strictEqual(rt.threads.length, 3); + t.strictEqual(rt.threads[1].status, Thread.STATUS_RUNNING); + t.strictEqual(th.status, Thread.STATUS_YIELD); + // yields when some restarted thread is active + th.status = Thread.STATUS_RUNNING; + e.broadcastAndWait({BROADCAST_OPTION: 'message'}, util); + t.strictEqual(th.status, Thread.STATUS_YIELD); + // does not yield once all threads are done + th.status = Thread.STATUS_RUNNING; + rt.threads[1].status = Thread.STATUS_DONE; + e.broadcastAndWait({BROADCAST_OPTION: 'message'}, util); + t.strictEqual(th.status, Thread.STATUS_RUNNING); + + t.end(); +}); From 34dfbb50baf0dcc6eea58860f25e84139765d4d3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 16 Nov 2017 14:17:08 -0500 Subject: [PATCH 607/721] Update naming and implementation for custom procedure blocks --- src/blocks/scratch3_procedures.js | 13 +++++++------ src/engine/blocks.js | 5 ++--- src/engine/thread.js | 4 ++-- src/serialization/sb2.js | 18 ++++++++++-------- src/serialization/sb2_specmap.js | 14 ++++++++++---- test/unit/engine_sequencer.js | 6 +++--- test/unit/engine_thread.js | 4 ++-- 7 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/blocks/scratch3_procedures.js b/src/blocks/scratch3_procedures.js index b91630f1..22e2f788 100644 --- a/src/blocks/scratch3_procedures.js +++ b/src/blocks/scratch3_procedures.js @@ -13,17 +13,18 @@ class Scratch3ProcedureBlocks { */ getPrimitives () { return { - procedures_defnoreturn: this.defNoReturn, - procedures_callnoreturn: this.callNoReturn, - procedures_param: this.param + procedures_definition: this.definition, + procedures_call: this.call, + argument_reporter_string_number: this.param, + argument_reporter_boolean: this.param }; } - defNoReturn () { + definition () { // No-op: execute the blocks. } - callNoReturn (args, util) { + call (args, util) { if (!util.stackFrame.executed) { const procedureCode = args.mutation.proccode; const paramNames = util.getProcedureParamNames(procedureCode); @@ -47,7 +48,7 @@ class Scratch3ProcedureBlocks { } param (args, util) { - const value = util.getParam(args.mutation.paramname); + const value = util.getParam(args.VALUE); return value; } } diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 92a2d58e..1aa97d28 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -152,8 +152,7 @@ class Blocks { for (const id in this._blocks) { if (!this._blocks.hasOwnProperty(id)) continue; const block = this._blocks[id]; - if (block.opcode === 'procedures_defnoreturn' || - block.opcode === 'procedures_defreturn') { + if (block.opcode === 'procedures_definition') { const internal = this._getCustomBlockInternal(block); if (internal && internal.mutation.proccode === name) { return id; // The outer define block id @@ -172,7 +171,7 @@ class Blocks { for (const id in this._blocks) { if (!this._blocks.hasOwnProperty(id)) continue; const block = this._blocks[id]; - if (block.opcode === 'procedures_callnoreturn_internal' && + if (block.opcode === 'procedures_prototype' && block.mutation.proccode === name) { return JSON.parse(block.mutation.argumentnames); } diff --git a/src/engine/thread.js b/src/engine/thread.js index 3b8ce32b..def1fc1e 100644 --- a/src/engine/thread.js +++ b/src/engine/thread.js @@ -164,7 +164,7 @@ class Thread { let blockID = this.peekStack(); while (blockID !== null) { const block = this.target.blocks.getBlock(blockID); - if (typeof block !== 'undefined' && block.opcode === 'procedures_callnoreturn') { + if (typeof block !== 'undefined' && block.opcode === 'procedures_call') { break; } this.popStack(); @@ -271,7 +271,7 @@ class Thread { const sp = this.stack.length - 1; for (let i = sp - 1; i >= 0; i--) { const block = this.target.blocks.getBlock(this.stack[i]); - if (block.opcode === 'procedures_callnoreturn' && + if (block.opcode === 'procedures_call' && block.mutation.proccode === procedureCode) { return true; } diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index 2a54d40a..16b63da8 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -518,7 +518,7 @@ const parseBlock = function (sb2block, getVariableId, extensions) { }; activeBlock.children = [{ id: inputUid, - opcode: 'procedures_callnoreturn_internal', + opcode: 'procedures_prototype', inputs: {}, fields: {}, next: null, @@ -544,13 +544,15 @@ const parseBlock = function (sb2block, getVariableId, extensions) { argumentids: JSON.stringify(parseProcedureArgIds(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'. - }; + // Assign correct opcode based on the block shape. + switch (sb2block[2]) { + case 'r': + activeBlock.opcode = 'argument_reporter_string_number'; + break; + case 'b': + activeBlock.opcode = 'argument_reporter_boolean'; + break; + } } return activeBlock; }; diff --git a/src/serialization/sb2_specmap.js b/src/serialization/sb2_specmap.js index b511d59c..70bb0345 100644 --- a/src/serialization/sb2_specmap.js +++ b/src/serialization/sb2_specmap.js @@ -1382,15 +1382,21 @@ const specMap = { ] }, 'procDef': { - opcode: 'procedures_defnoreturn', + opcode: 'procedures_definition', argMap: [] }, 'getParam': { - opcode: 'procedures_param', - argMap: [] + // Doesn't map to single opcode. Import step assigns final correct opcode. + opcode: 'argument_reporter_string_number', + argMap: [ + { + type: 'field', + fieldName: 'VALUE' + } + ] }, 'call': { - opcode: 'procedures_callnoreturn', + opcode: 'procedures_call', argMap: [] } }; diff --git a/test/unit/engine_sequencer.js b/test/unit/engine_sequencer.js index abf7102d..751e9bb6 100644 --- a/test/unit/engine_sequencer.js +++ b/test/unit/engine_sequencer.js @@ -36,7 +36,7 @@ const generateBlock = function (id) { block: 'fakeBlock', name: 'fakeName', next: null, - opcode: 'procedures_defnoreturn', + opcode: 'procedures_definition', mutation: {proccode: 'fakeCode'}, parent: null, shadow: false, @@ -55,7 +55,7 @@ const generateBlockInput = function (id, next, inp) { block: 'fakeBlock', name: 'fakeName', next: next, - opcode: 'procedures_defnoreturn', + opcode: 'procedures_definition', mutation: {proccode: 'fakeCode'}, parent: null, shadow: false, @@ -156,7 +156,7 @@ test('stepToProcedure', t => { th.target.blocks.createBlock({ id: 'internalId', - opcode: 'procedures_callnoreturn_internal', + opcode: 'procedures_prototype', mutation: { proccode: 'othercode' } diff --git a/test/unit/engine_thread.js b/test/unit/engine_thread.js index 60419d36..6c9a9e4f 100644 --- a/test/unit/engine_thread.js +++ b/test/unit/engine_thread.js @@ -194,7 +194,7 @@ test('stopThisScript', t => { block: 'fakeBlock', name: 'STEPS', next: null, - opcode: 'procedures_callnoreturn', + opcode: 'procedures_call', mutation: {proccode: 'fakeCode'}, parent: null, shadow: false, @@ -246,7 +246,7 @@ test('isRecursiveCall', t => { block: 'fakeBlock', name: 'STEPS', next: null, - opcode: 'procedures_callnoreturn', + opcode: 'procedures_call', mutation: {proccode: 'fakeCode'}, parent: null, shadow: false, From 91c93830a8bdc6c1272f08ad1ff935915fabea5d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 16 Nov 2017 14:23:56 -0500 Subject: [PATCH 608/721] Fix tests --- test/unit/blocks_procedures.js | 2 +- test/unit/engine_thread.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/blocks_procedures.js b/test/unit/blocks_procedures.js index 4ecdb41d..cab5e04e 100644 --- a/test/unit/blocks_procedures.js +++ b/test/unit/blocks_procedures.js @@ -22,7 +22,7 @@ test('calling a custom block with no definition does not throw', t => { } }; t.doesNotThrow(() => { - blocks.callNoReturn(args, util); + blocks.call(args, util); }); t.end(); }); diff --git a/test/unit/engine_thread.js b/test/unit/engine_thread.js index 6c9a9e4f..533f7252 100644 --- a/test/unit/engine_thread.js +++ b/test/unit/engine_thread.js @@ -139,7 +139,7 @@ test('goToNextBlock', t => { block: 'fakeBlock', name: 'STEPS', next: null, - opcode: 'procedures_callnoreturn', + opcode: 'procedures_call', mutation: {proccode: 'fakeCode'}, parent: null, shadow: false, From 255e160ebb00c123e373a5199b66e18b1e0ca629 Mon Sep 17 00:00:00 2001 From: DD Date: Thu, 16 Nov 2017 17:19:51 -0500 Subject: [PATCH 609/721] Change it so we dont have to do string manipulation on IDs to get data out of events --- src/blocks/scratch3_looks.js | 8 ++++---- src/blocks/scratch3_motion.js | 6 +++--- src/blocks/scratch3_sensing.js | 10 +++++----- src/blocks/scratch3_sound.js | 2 +- src/engine/blocks.js | 11 +++++------ src/virtual-machine.js | 9 --------- 6 files changed, 18 insertions(+), 28 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index a64f095b..6c8f4fe3 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -244,10 +244,10 @@ class Scratch3LooksBlocks { getMonitored () { return { - size: {isSpriteSpecific: true}, - costumeorder: {isSpriteSpecific: true}, - backdroporder: {}, - backdropname: {} + looks_size: {isSpriteSpecific: true}, + looks_costumeorder: {isSpriteSpecific: true}, + looks_backdroporder: {}, + looks_backdropname: {} }; } diff --git a/src/blocks/scratch3_motion.js b/src/blocks/scratch3_motion.js index b30138d3..0b206975 100644 --- a/src/blocks/scratch3_motion.js +++ b/src/blocks/scratch3_motion.js @@ -40,9 +40,9 @@ class Scratch3MotionBlocks { getMonitored () { return { - xposition: {isSpriteSpecific: true}, - yposition: {isSpriteSpecific: true}, - direction: {isSpriteSpecific: true} + motion_xposition: {isSpriteSpecific: true}, + motion_yposition: {isSpriteSpecific: true}, + motion_direction: {isSpriteSpecific: true} }; } diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index d51dd5d3..fa129f6c 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -51,11 +51,11 @@ class Scratch3SensingBlocks { getMonitored () { return { - answer: {}, - loudness: {}, - timer: {}, - of: {}, - current: {} + sensing_answer: {}, + sensing_loudness: {}, + sensing_timer: {}, + sensing_of: {}, + sensing_current: {} }; } diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index d3f3f92a..be78ec7e 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -105,7 +105,7 @@ class Scratch3SoundBlocks { getMonitored () { return { - volume: {} + sound_volume: {} }; } diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 155a2e5e..a5b6ad45 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -327,7 +327,11 @@ class Blocks { break; case 'checkbox': block.isMonitored = args.value; - block.targetId = args.isSpriteSpecific ? this._getTargetIdFromBlockId(block.id) : null; + if (optRuntime) { + const isSpriteSpecific = optRuntime.monitorBlockInfo.hasOwnProperty(block.opcode) && + optRuntime.monitorBlockInfo[block.opcode].isSpriteSpecific; + block.targetId = isSpriteSpecific ? optRuntime.getEditingTarget().id : null; + } if (optRuntime && wasMonitored && !block.isMonitored) { optRuntime.requestRemoveMonitor(block.id); } else if (optRuntime && !wasMonitored && block.isMonitored) { @@ -416,11 +420,6 @@ class Blocks { }); } - _getTargetIdFromBlockId (blockId) { - // First word of block ID. See makeToolboxXML in scratch-gui - return blockId.split('_')[0]; - } - /** * Block management: delete blocks and their associated scripts. * @param {!object} e Blockly delete event to be processed. diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 5c5361ba..994de986 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -578,15 +578,6 @@ class VirtualMachine extends EventEmitter { // Filter events by type, since monitor blocks only need to listen to these events. // Monitor blocks shouldn't be destroyed when flyout blocks are deleted. if (['create', 'change'].indexOf(e.type) !== -1) { - let blockType = e.blockId.split('_'); - blockType = blockType[blockType.length - 1]; - if (!this.runtime.monitorBlockInfo.hasOwnProperty(blockType)) { - return; - } - if (!this.runtime.monitorBlockInfo.hasOwnProperty(blockType) || - this.runtime.monitorBlockInfo[blockType].isSpriteSpecific) { - e.isSpriteSpecific = true; - } this.runtime.monitorBlocks.blocklyListen(e, this.runtime); } } From 8963d0222f8f07e9b3e552036d8969133b4636d3 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Fri, 17 Nov 2017 14:24:32 +0000 Subject: [PATCH 610/721] chore(package): update copy-webpack-plugin to version 4.2.1 Closes #682 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 657022b1..5e67750c 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "babel-eslint": "^7.1.1", "babel-loader": "^7.0.0", "babel-preset-es2015": "^6.24.1", - "copy-webpack-plugin": "4.0.1", + "copy-webpack-plugin": "4.2.1", "escape-html": "1.0.3", "eslint": "^4.5.0", "eslint-config-scratch": "^4.0.0", From 26771e9e0c9ccc7bc984686a6a6895a44543a95d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 17 Nov 2017 09:59:09 -0500 Subject: [PATCH 611/721] Only uglify if building for production. Uglify was trying to run always, including the dev-server output, which included es6 that broke uglifier. --- webpack.config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index b58666d4..7166587f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -20,12 +20,12 @@ const base = { } }] }, - plugins: [ + plugins: process.env.NODE_ENV === 'production' ? [ new webpack.optimize.UglifyJsPlugin({ include: /\.min\.js$/, minimize: true }) - ] + ] : [] }; module.exports = [ From a036564a600c63cb9dd4282487a331a0ffb67275 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 17 Nov 2017 10:06:38 -0500 Subject: [PATCH 612/721] Add node env to travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 955aa5ba..e1a2e3b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ node_js: env: - NPM_SCRIPT="tap:unit -- --jobs=4" - NPM_SCRIPT="tap:integration -- --jobs=4" + - NODE_ENV=production sudo: false cache: directories: From d83f32c578b2c9168d750af5e1cd48394d4214d0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 17 Nov 2017 11:48:22 -0500 Subject: [PATCH 613/721] Use production=false in install step --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e1a2e3b8..4f98c136 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,8 @@ cache: directories: - node_modules install: -- npm install -- npm update +- npm --production=false install +- npm --production=false update script: npm run $NPM_SCRIPT jobs: include: From b19af399fad3868f590858aa0dd17062ae3ee421 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 17 Nov 2017 12:12:07 -0800 Subject: [PATCH 614/721] Make _removeThread safe during thread iteration Before: `_removeThread` stops a thread and immediately removes it from the runtime's thread array. If this happens while iterating over the thread array, such as in the case of clone deletion, some elements of the array can be missed. After: `_removeThread` has been renamed to `_stopThread`. It still stops the thread immediately but it no longer removes the thread from the runtime's thread array. Instead, `_stopThread` marks the thread for later removal. Such threads are removed at the top of the next `_step`. --- src/engine/runtime.js | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 2f34cd8d..4a223325 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -786,17 +786,14 @@ class Runtime extends EventEmitter { } /** - * Remove a thread from the list of threads. - * @param {?Thread} thread Thread object to remove from actives + * Stop a thread: stop running it immediately, and remove it from the thread list later. + * @param {!Thread} thread Thread object to remove from actives */ - _removeThread (thread) { + _stopThread (thread) { + // Mark the thread for later removal + thread.isKilled = true; // Inform sequencer to stop executing that thread. this.sequencer.retireThread(thread); - // Remove from the list. - const i = this.threads.indexOf(thread); - if (i > -1) { - this.threads.splice(i, 1); - } } /** @@ -859,7 +856,7 @@ class Runtime extends EventEmitter { // edge activated hat thread that runs every frame continue; } - this._removeThread(this.threads[i]); + this._stopThread(this.threads[i]); return; } } @@ -1035,8 +1032,7 @@ class Runtime extends EventEmitter { continue; } if (this.threads[i].target === target) { - this.threads[i].isKilled = true; - this._removeThread(this.threads[i]); + this._stopThread(this.threads[i]); } } } @@ -1075,11 +1071,7 @@ class Runtime extends EventEmitter { } this.targets = newTargets; // Dispose all threads. - const threadsCopy = this.threads.slice(); - while (threadsCopy.length > 0) { - const poppedThread = threadsCopy.pop(); - this._removeThread(poppedThread); - } + this.threads.forEach(thread => this._stopThread(thread)); } /** @@ -1093,6 +1085,10 @@ class Runtime extends EventEmitter { } this.profiler.start(stepProfilerId); } + + // Clean up threads that were told to stop during or since the last step + this.threads = this.threads.filter(thread => !thread.isKilled); + // Find all edge-activated hats, and add them to threads to be evaluated. for (const hatType in this._hats) { if (!this._hats.hasOwnProperty(hatType)) continue; From 0959c4dbd5116ee38990c9cdd5efedb742f6945e Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Fri, 17 Nov 2017 21:59:41 +0000 Subject: [PATCH 615/721] chore(package): update gh-pages to version 1.1.0 Closes #559 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 657022b1..efc818f7 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "eslint": "^4.5.0", "eslint-config-scratch": "^4.0.0", "expose-loader": "0.7.3", - "gh-pages": "^0.12.0", + "gh-pages": "^1.1.0", "got": "5.7.1", "highlightjs": "^9.8.0", "htmlparser2": "3.9.2", From e052f1d53bc38c326678bb230de9650f39cc93c7 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Sat, 18 Nov 2017 07:41:49 +0000 Subject: [PATCH 616/721] chore(package): update expose-loader to version 0.7.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 657022b1..825ff303 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "escape-html": "1.0.3", "eslint": "^4.5.0", "eslint-config-scratch": "^4.0.0", - "expose-loader": "0.7.3", + "expose-loader": "0.7.4", "gh-pages": "^0.12.0", "got": "5.7.1", "highlightjs": "^9.8.0", From 6ed2ca6caaf2fe5d2082215c8b43508a74f061c5 Mon Sep 17 00:00:00 2001 From: griffpatch Date: Mon, 20 Nov 2017 15:22:51 +0000 Subject: [PATCH 617/721] Cache Block Inputs & Procedure Definitions * Cache Block Inputs & Procedure Definitions * @mzgoddard requested changes on this pull request * Move all caching into a single reusable field _cache. * Invalidate 'all' caches on any change. * Use 'typeof' instead of hasOwnProperty. * Take caching out of Block and use lookup instead. --- src/engine/blocks.js | 73 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 2f2de7b4..8776c6f6 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -25,6 +25,30 @@ class Blocks { * @type {Array.} */ this._scripts = []; + + /** + * Runtime Cache + * @type {{inputs: {}, procedureParamNames: {}, procedureDefinitions: {}}} + * @private + */ + this._cache = { + /** + * Cache block inputs by block id + * @type {object.>} + */ + inputs: {}, + /** + * Cache procedure Param Names by block id + * @type {object.>} + */ + procedureParamNames: {}, + /** + * Cache procedure definitions by block id + * @type {object.} + */ + procedureDefinitions: {} + }; + } /** @@ -105,11 +129,16 @@ class Blocks { /** * Get all non-branch inputs for a block. * @param {?object} block the block to query. - * @return {!object} All non-branch inputs and their associated blocks. + * @return {?Array.} All non-branch inputs and their associated blocks. */ getInputs (block) { if (typeof block === 'undefined') return null; - const inputs = {}; + let inputs = this._cache.inputs[block.id]; + if (typeof inputs !== 'undefined') { + return inputs; + } + + inputs = {}; for (const input in block.inputs) { // Ignore blocks prefixed with branch prefix. if (input.substring(0, Blocks.BRANCH_INPUT_PREFIX.length) !== @@ -117,6 +146,8 @@ class Blocks { inputs[input] = block.inputs[input]; } } + + this._cache.inputs[block.id] = inputs; return inputs; } @@ -149,16 +180,24 @@ class Blocks { * @return {?string} ID of procedure definition. */ getProcedureDefinition (name) { + const blockID = this._cache.procedureDefinitions[name]; + if (typeof blockID !== 'undefined') { + return blockID; + } + for (const id in this._blocks) { if (!this._blocks.hasOwnProperty(id)) continue; const block = this._blocks[id]; if (block.opcode === 'procedures_definition') { const internal = this._getCustomBlockInternal(block); if (internal && internal.mutation.proccode === name) { - return id; // The outer define block id + this._cache.procedureDefinitions[name] = id; // The outer define block id + return id; } } } + + this._cache.procedureDefinitions[name] = null; return null; } @@ -168,14 +207,23 @@ class Blocks { * @return {?Array.} List of param names for a procedure. */ getProcedureParamNames (name) { + const cachedNames = this._cache.procedureParamNames[name]; + if (typeof cachedNames !== 'undefined') { + return cachedNames; + } + for (const id in this._blocks) { if (!this._blocks.hasOwnProperty(id)) continue; const block = this._blocks[id]; if (block.opcode === 'procedures_prototype' && block.mutation.proccode === name) { - return JSON.parse(block.mutation.argumentnames); + const paramNames = JSON.parse(block.mutation.argumentnames); + this._cache.procedureParamNames[name] = paramNames; + return paramNames; } } + + this._cache.procedureParamNames[name] = null; return null; } @@ -271,6 +319,15 @@ class Blocks { // --------------------------------------------------------------------- + /** + * Reset all runtime caches. + */ + resetCache () { + this._cache.inputs = {}; + this._cache.procedureParamNames = {}; + this._cache.procedureDefinitions = {}; + } + /** * Block management: create blocks and scripts from a `create` event * @param {!object} block Blockly create event to be processed @@ -289,6 +346,8 @@ class Blocks { if (block.topLevel) { this._addScript(block.id); } + + this.resetCache(); } /** @@ -301,7 +360,6 @@ class Blocks { if (['field', 'mutation', 'checkbox'].indexOf(args.element) === -1) return; const block = this._blocks[args.id]; if (typeof block === 'undefined') return; - const wasMonitored = block.isMonitored; switch (args.element) { case 'field': @@ -337,6 +395,8 @@ class Blocks { } break; } + + this.resetCache(); } /** @@ -393,6 +453,7 @@ class Blocks { } this._blocks[e.id].parent = e.newParent; } + this.resetCache(); } @@ -447,6 +508,8 @@ class Blocks { // Delete block itself. delete this._blocks[blockId]; + + this.resetCache(); } // --------------------------------------------------------------------- From 6d260231049040d38c40181275a8f20e4c7ff1ee Mon Sep 17 00:00:00 2001 From: DD Date: Mon, 20 Nov 2017 13:00:37 -0500 Subject: [PATCH 618/721] revert unnecessary changes' : --- src/engine/blocks.js | 3 +-- src/util/uid.js | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index a5b6ad45..89d7a63c 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -226,8 +226,7 @@ class Blocks { id: e.blockId, element: e.element, name: e.name, - value: e.newValue, - isSpriteSpecific: e.isSpriteSpecific + value: e.newValue }, optRuntime); break; case 'move': diff --git a/src/util/uid.js b/src/util/uid.js index 34e8eaf5..fd4c41e8 100644 --- a/src/util/uid.js +++ b/src/util/uid.js @@ -6,10 +6,9 @@ * Legal characters for the unique ID. * Should be all on a US keyboard. No XML special characters or control codes. * Removed $ due to issue 251. - * Removed _ which denotes word separation in XML. * @private */ -const soup_ = '!#%()*+,-./:;=?@[]^`{|}~' + +const soup_ = '!#%()*+,-./:;=?@[]^_`{|}~' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; /** From c3a72560f9d8902a77557a4977e44de7f64671fa Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 20 Nov 2017 13:42:35 -0500 Subject: [PATCH 619/721] Clarify @TODO comment about extension status indicator --- src/extensions/scratch3_music/index.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index ab461441..b209e3cd 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -45,9 +45,7 @@ class Scratch3MusicBlocks { /** * Download and decode the full set of drum sounds, and store the audio buffers * in the drum buffers array. - * @TODO: Also load the instrument sounds here (rename this fn), and use Promise.all - * to detect that all the assets have loaded in order to update the extension status - * indicator. + * @TODO: Also load the instrument sounds here (rename this fn) */ _loadAllDrumSounds () { const loadingPromises = []; @@ -56,7 +54,7 @@ class Scratch3MusicBlocks { loadingPromises.push(promise); }); Promise.all(loadingPromises).then(() => { - // done! + // @TODO: Update the extension status indicator. }); } From 1e00a0c2526cd4db75a3fc0496076354b97e6f59 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 20 Nov 2017 13:44:44 -0500 Subject: [PATCH 620/721] Comment for no-op above concurrency limit --- src/extensions/scratch3_music/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index b209e3cd..fc07b647 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -458,6 +458,7 @@ class Scratch3MusicBlocks { */ _playDrumNum (util, drumNum) { if (util.target.audioPlayer === null) return; + // If we're playing too many sounds, do not play the drum sound. if (this._drumConcurrencyCounter > Scratch3MusicBlocks.CONCURRENCY_LIMIT) { return; } From b6cf12202c39d454b8ea7773a4f9995a53680bb2 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 20 Nov 2017 13:50:46 -0500 Subject: [PATCH 621/721] Rename unit test file --- test/unit/{blocks_music.js => extension_music.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/unit/{blocks_music.js => extension_music.js} (100%) diff --git a/test/unit/blocks_music.js b/test/unit/extension_music.js similarity index 100% rename from test/unit/blocks_music.js rename to test/unit/extension_music.js From a8c629dc9bf8534ec19317787af5624002436afc Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 20 Nov 2017 17:04:17 -0500 Subject: [PATCH 622/721] Replace 'got' module with 'nets'. Resolves GH-802 --- .gitignore | 1 + package.json | 2 +- src/io/deviceManager.js | 14 ++++++++++++-- test/unit/io_deviceManager.js | 13 +++++++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 2f382587..a733474c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .DS_Store # NPM +package-lock.json /node_modules npm-* diff --git a/package.json b/package.json index efc818f7..4bb733f8 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "eslint-config-scratch": "^4.0.0", "expose-loader": "0.7.3", "gh-pages": "^1.1.0", - "got": "5.7.1", "highlightjs": "^9.8.0", "htmlparser2": "3.9.2", "immutable": "3.8.1", @@ -44,6 +43,7 @@ "json": "^9.0.4", "lodash.defaultsdeep": "4.6.0", "minilog": "3.1.0", + "nets": "3.2.0", "promise": "7.1.1", "scratch-audio": "latest", "scratch-blocks": "latest", diff --git a/src/io/deviceManager.js b/src/io/deviceManager.js index d2d0046f..2ba018ce 100644 --- a/src/io/deviceManager.js +++ b/src/io/deviceManager.js @@ -1,4 +1,4 @@ -const got = require('got'); +const nets = require('nets'); const io = require('socket.io-client/dist/socket.io'); const querystring = require('querystring'); @@ -312,7 +312,17 @@ class DeviceManager { }; if (deviceSpec) queryObject.spec = deviceSpec; const url = `${this._serverURL}/${encodeURIComponent(deviceType)}/list?${querystring.stringify(queryObject)}`; - return got(url).then(response => JSON.parse(response.body)); + return new Promise((resolve, reject) => { + nets({ + method: 'GET', + url: url, + json: {} + }, (err, res, body) => { + if (err) return reject(err); + if (res.statusCode !== 200) reject(body); + resolve(body); + }); + }); } /** diff --git a/test/unit/io_deviceManager.js b/test/unit/io_deviceManager.js index f013ef5d..f4d32d06 100644 --- a/test/unit/io_deviceManager.js +++ b/test/unit/io_deviceManager.js @@ -19,3 +19,16 @@ test('default connected', t => { t.strictEqual(deviceManager.isConnected, true); t.end(); }); + +test('list', t => { + const deviceManager = new DeviceManager(); + deviceManager + .list('test', 'test', null) + .catch(err => { + t.true((typeof err === 'undefined') || (typeof err === 'object')); + }) + .then(body => { + t.true((typeof body === 'undefined') || (typeof body === 'object')); + t.end(); + }); +}); From ec9d8094bd9e41a87856c45defbe37ec5287aa03 Mon Sep 17 00:00:00 2001 From: DD Date: Mon, 20 Nov 2017 17:23:59 -0500 Subject: [PATCH 623/721] fix review comments --- src/engine/runtime.js | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index d8fa8835..63aec3bb 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -232,9 +232,6 @@ class Runtime extends EventEmitter { // Register all given block packages. this._registerBlockPackages(); - // Populate monitorBlockInfo - this._registerMonitorInfo(); - // Register and initialize "IO devices", containers for processing // I/O related data. /** @type {Object.} */ @@ -407,6 +404,10 @@ class Runtime extends EventEmitter { } } } + // Collect monitored from package. + if (packageObject.getMonitored) { + this.monitorBlockInfo = Object.assign({}, this.monitorBlockInfo, packageObject.getMonitored()); + } } } } @@ -461,22 +462,6 @@ class Runtime extends EventEmitter { this.emit(Runtime.EXTENSION_ADDED, categoryInfo.blocks.concat(categoryInfo.menus)); } - /** - * Populate this.monitorBlockInfo - */ - _registerMonitorInfo () { - for (const packageName in defaultBlockPackages) { - if (defaultBlockPackages.hasOwnProperty(packageName)) { - // @todo pass a different runtime depending on package privilege? - const packageObject = new (defaultBlockPackages[packageName])(this); - // Collect monitored from package. - if (packageObject.getMonitored) { - this.monitorBlockInfo = Object.assign({}, this.monitorBlockInfo, packageObject.getMonitored()); - } - } - } - } - /** * Build the scratch-blocks JSON for a menu. Note that scratch-blocks treats menus as a special kind of block. * @param {string} menuName - the name of the menu @@ -864,7 +849,7 @@ class Runtime extends EventEmitter { /** * Enqueue a script that when finished will update the monitor for the block. * @param {!string} topBlockId ID of block that starts the script. - * @param {?string} optTarget target Target to run script on. If not supplied, uses editing target. + * @param {?Target} optTarget target Target to run script on. If not supplied, uses editing target. */ addMonitorScript (topBlockId, optTarget) { if (!optTarget) optTarget = this._editingTarget; From 73565460729138f6ec30f74eef4fbf361d370886 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Thu, 9 Nov 2017 18:35:12 -0500 Subject: [PATCH 624/721] Add benchmark prototype Build a page with webpack that loads a project and automatically runs it, collecting performance information for a set amount of time, stopping and displaying the results. --- src/benchmark/benchmark.css | 7 + src/benchmark/benchmark.js | 460 ++++++++++++++++++++++++++++++++++++ src/benchmark/index.html | 69 ++++++ webpack.config.js | 63 +++++ 4 files changed, 599 insertions(+) create mode 100644 src/benchmark/benchmark.css create mode 100644 src/benchmark/benchmark.js create mode 100644 src/benchmark/index.html diff --git a/src/benchmark/benchmark.css b/src/benchmark/benchmark.css new file mode 100644 index 00000000..e843b5e9 --- /dev/null +++ b/src/benchmark/benchmark.css @@ -0,0 +1,7 @@ +@media (min-width: 960px) { + .profile-tables { + position: absolute; + top: 0; + left: 520px; + } +} diff --git a/src/benchmark/benchmark.js b/src/benchmark/benchmark.js new file mode 100644 index 00000000..d8540404 --- /dev/null +++ b/src/benchmark/benchmark.js @@ -0,0 +1,460 @@ +const Scratch = window.Scratch = window.Scratch || {}; + +const ASSET_SERVER = 'https://cdn.assets.scratch.mit.edu/'; +const PROJECT_SERVER = 'https://cdn.projects.scratch.mit.edu/'; + +const SLOW = .1; + +const loadProject = function () { + let id = location.hash.substring(1); + if (id.length < 1 || !isFinite(id)) { + id = '119615668'; + } + Scratch.vm.downloadProjectId(id); +}; + +/** + * @param {Asset} asset - calculate a URL for this asset. + * @returns {string} a URL to download a project file. + */ +const getProjectUrl = function (asset) { + const assetIdParts = asset.assetId.split('.'); + const assetUrlParts = [PROJECT_SERVER, 'internalapi/project/', assetIdParts[0], '/get/']; + if (assetIdParts[1]) { + assetUrlParts.push(assetIdParts[1]); + } + return assetUrlParts.join(''); +}; + +/** + * @param {Asset} asset - calculate a URL for this asset. + * @returns {string} a URL to download a project asset (PNG, WAV, etc.) + */ +const getAssetUrl = function (asset) { + const assetUrlParts = [ + ASSET_SERVER, + 'internalapi/asset/', + asset.assetId, + '.', + asset.dataFormat, + '/get/' + ]; + return assetUrlParts.join(''); +}; + +class LoadingProgress { + constructor (callback) { + this.total = 0; + this.complete = 0; + this.callback = callback; + } + + on (storage) { + const _this = this; + const _load = storage.webHelper.load; + storage.webHelper.load = function (...args) { + const result = _load.call(this, ...args); + _this.total += 1; + _this.callback(_this); + result.then(() => { + _this.complete += 1; + _this.callback(_this); + }); + return result; + }; + } +} + +class StatTable { + constructor ({table, keys, viewOf, isSlow}) { + this.table = table; + if (keys) { + this.keys = keys; + } + if (viewOf) { + this.viewOf = viewOf; + } + if (isSlow) { + this.isSlow = isSlow; + } + } + + render () { + const table = this.table; + Array.from(table.children) + .forEach(node => table.removeChild(node)); + const keys = this.keys(); + for (const key of keys) { + this.viewOf(key).render({ + table, + isSlow: frame => this.isSlow(key, frame) + }); + } + } +} + +class StatView { + constructor (name) { + this.name = name; + this.executions = 0; + this.selfTime = 0; + this.totalTime = 0; + } + + update (selfTime, totalTime) { + this.executions++; + this.selfTime += selfTime; + this.totalTime += totalTime; + } + + render ({table, isSlow}) { + const row = document.createElement('tr'); + let cell = document.createElement('td'); + cell.innerText = this.name; + row.appendChild(cell); + + if (isSlow(this)) { + row.setAttribute('class', 'slow'); + } + + cell = document.createElement('td'); + cell.innerText = (this.selfTime / 1000).toPrecision(3); + row.appendChild(cell); + + cell = document.createElement('td'); + cell.innerText = (this.totalTime / 1000).toPrecision(3); + row.appendChild(cell); + + cell = document.createElement('td'); + cell.innerText = this.executions; + row.appendChild(cell); + + table.appendChild(row); + } +} + +class RunningStats { + constructor (profiler) { + this.stepThreadsInnerId = profiler.idByName('Sequencer.stepThreads#inner'); + this.blockFunctionId = profiler.idByName('blockFunction'); + this.stpeThreadsId = profiler.idByName('Sequencer.stepThreads'); + + this.recordedTime = 0; + this.executed = { + steps: 0, + blocks: 0 + }; + } + + update (id, selfTime, totalTime) { + if (id === this.stpeThreadsId) { + this.recordedTime += totalTime; + } else if (id === this.stepThreadsInnerId) { + this.executed.steps++; + } else if (id === this.blockFunctionId) { + this.executed.blocks++; + } + } +} + +const WORK_TIME = 0.75; + +class RunningStatsView { + constructor ({runningStats, maxRecordedTime, dom}) { + this.recordedTimeDom = + dom.getElementsByClassName('profile-count-amount-recorded')[0]; + this.stepsLoopedDom = + dom.getElementsByClassName('profile-count-steps-looped')[0]; + this.blocksExecutedDom = + dom.getElementsByClassName('profile-count-blocks-executed')[0]; + + this.maxRecordedTime = maxRecordedTime; + this.maxWorkedTime = maxRecordedTime * WORK_TIME; + this.runningStats = runningStats; + } + + render () { + const { + runningStats, + recordedTimeDom, + stepsLoopedDom, + blocksExecutedDom + } = this; + const {executed} = runningStats; + const fractionWorked = runningStats.recordedTime / this.maxWorkedTime; + recordedTimeDom.innerText = `${(fractionWorked * 100).toFixed(1)} %`; + stepsLoopedDom.innerText = executed.steps; + blocksExecutedDom.innerText = executed.blocks; + } +} + +class Frames { + constructor (profiler) { + this.profiler = profiler; + + this.frames = []; + } + + update (id, selfTime, totalTime) { + if (!this.frames[id]) { + this.frames[id] = new StatView(this.profiler.nameById(id)); + } + this.frames[id].update(selfTime, totalTime); + } +} + +const frameOrder = [ + 'blockFunction', + 'execute', + 'Sequencer.stepThread', + 'Sequencer.stepThreads#inner', + 'Sequencer.stepThreads', + 'RenderWebGL.draw', + 'Runtime._step' +]; + +class FramesTable extends StatTable { + constructor (options) { + super(options); + + this.profiler = options.profiler; + this.frames = options.frames; + } + + keys () { + const keys = Object.keys(this.frames.frames) + .map(id => this.profiler.nameById(Number(id))); + keys.sort((a, b) => frameOrder.indexOf(a) - frameOrder.indexOf(b)); + return keys; + } + + viewOf (key) { + return this.frames.frames[this.profiler.idByName(key)]; + } + + isSlow (key, frame) { + return ([ + 'Sequencer.stepThreads', + 'Sequencer.stepThreads#inner', + 'Sequencer.stepThread', + 'execute' + ].indexOf(key) > 0 && + frame.selfTime / frame.totalTime > SLOW); + } +} + +class Opcodes { + constructor (profiler) { + this.blockFunctionId = profiler.idByName('blockFunction'); + + this.opcodes = {}; + } + + update (id, selfTime, totalTime, arg) { + if (id === this.blockFunctionId) { + if (!this.opcodes[arg]) { + this.opcodes[arg] = new StatView(arg); + } + this.opcodes[arg].update(selfTime, totalTime); + } + } +} + +class OpcodeTable extends StatTable { + constructor (options) { + super(options); + + this.profiler = options.profiler; + this.opcodes = options.opcodes; + this.frames = options.frames; + } + + keys () { + const keys = Object.keys(this.opcodes.opcodes); + keys.sort(); + return keys; + } + + viewOf (key) { + return this.opcodes.opcodes[key]; + } + + isSlow (key) { + const blockFunctionTotalTime = this.frames.frames[this.profiler.idByName('blockFunction')].totalTime; + const rowTotalTime = this.opcodes.opcodes[key].totalTime; + const percentOfRun = rowTotalTime / blockFunctionTotalTime; + return percentOfRun > SLOW; + } +} + +class ProfilerRun { + constructor ({vm, maxRecordedTime, warmUpTime}) { + this.vm = vm; + this.maxRecordedTime = maxRecordedTime; + this.warmUpTime = warmUpTime; + + vm.runtime.enableProfiling(); + const profiler = this.profiler = vm.runtime.profiler; + vm.runtime.profiler = null; + + const runningStats = this.runningStats = new RunningStats(profiler); + const runningStatsView = this.runningStatsView = new RunningStatsView({ + dom: document.getElementsByClassName('profile-count-group')[0], + + runningStats, + maxRecordedTime: 6000 + }); + + const frames = this.frames = new Frames(profiler); + this.frameTable = new FramesTable({ + table: document + .getElementsByClassName('profile-count-frame-table')[0] + .getElementsByTagName('tbody')[0], + + profiler, + frames + }); + + const opcodes = this.opcodes = new Opcodes(profiler); + this.opcodeTable = new OpcodeTable({ + table: document + .getElementsByClassName('profile-count-opcode-table')[0] + .getElementsByTagName('tbody')[0], + + profiler, + opcodes, + frames + }); + + const stepId = profiler.idByName('Runtime._step'); + profiler.onFrame = ({id, selfTime, totalTime, arg}) => { + if (id === stepId) { + runningStatsView.render(); + } + runningStats.update(id, selfTime, totalTime, arg); + opcodes.update(id, selfTime, totalTime, arg); + frames.update(id, selfTime, totalTime, arg); + }; + } + + run () { + loadProject(); + + this.vm.on('workspaceUpdate', () => { + setTimeout(() => { + this.vm.greenFlag(); + }, 100); + setTimeout(() => { + this.vm.runtime.profiler = this.profiler; + }, 100 + this.warmUpTime); + setTimeout(() => { + this.vm.stopAll(); + clearTimeout(this.vm.runtime._steppingInterval); + this.vm.runtime.profiler = null; + + this.frameTable.render(); + this.opcodeTable.render(); + }, 100 + this.warmUpTime + this.maxRecordedTime); + }); + } +} + +window.onload = function () { + // Lots of global variables to make debugging easier + // Instantiate the VM. + const vm = new window.VirtualMachine(); + Scratch.vm = vm; + + vm.setTurboMode(true); + + const storage = new ScratchStorage(); /* global ScratchStorage */ + const AssetType = storage.AssetType; + storage.addWebSource([AssetType.Project], getProjectUrl); + storage.addWebSource([AssetType.ImageVector, AssetType.ImageBitmap, AssetType.Sound], getAssetUrl); + vm.attachStorage(storage); + + new LoadingProgress(progress => { + document.getElementsByClassName('loading-total')[0] + .innerText = progress.total; + document.getElementsByClassName('loading-complete')[0] + .innerText = progress.complete; + }).on(storage); + + new ProfilerRun({ + vm, + warmUpTime: 4000, + maxRecordedTime: 6000 + }).run(); + + // Instantiate the renderer and connect it to the VM. + const canvas = document.getElementById('scratch-stage'); + const renderer = new window.RenderWebGL(canvas); + Scratch.renderer = renderer; + vm.attachRenderer(renderer); + const audioEngine = new window.AudioEngine(); + vm.attachAudioEngine(audioEngine); + + // Feed mouse events as VM I/O events. + document.addEventListener('mousemove', e => { + const rect = canvas.getBoundingClientRect(); + const coordinates = { + x: e.clientX - rect.left, + y: e.clientY - rect.top, + canvasWidth: rect.width, + canvasHeight: rect.height + }; + Scratch.vm.postIOData('mouse', coordinates); + }); + canvas.addEventListener('mousedown', e => { + const rect = canvas.getBoundingClientRect(); + const data = { + isDown: true, + x: e.clientX - rect.left, + y: e.clientY - rect.top, + canvasWidth: rect.width, + canvasHeight: rect.height + }; + Scratch.vm.postIOData('mouse', data); + e.preventDefault(); + }); + canvas.addEventListener('mouseup', e => { + const rect = canvas.getBoundingClientRect(); + const data = { + isDown: false, + x: e.clientX - rect.left, + y: e.clientY - rect.top, + canvasWidth: rect.width, + canvasHeight: rect.height + }; + Scratch.vm.postIOData('mouse', data); + e.preventDefault(); + }); + + // Feed keyboard events as VM I/O events. + document.addEventListener('keydown', e => { + // Don't capture keys intended for Blockly inputs. + if (e.target !== document && e.target !== document.body) { + return; + } + Scratch.vm.postIOData('keyboard', { + keyCode: e.keyCode, + isDown: true + }); + e.preventDefault(); + }); + document.addEventListener('keyup', e => { + // Always capture up events, + // even those that have switched to other targets. + Scratch.vm.postIOData('keyboard', { + keyCode: e.keyCode, + isDown: false + }); + // E.g., prevent scroll. + if (e.target !== document && e.target !== document.body) { + e.preventDefault(); + } + }); + + // Run threads + vm.start(); +}; diff --git a/src/benchmark/index.html b/src/benchmark/index.html new file mode 100644 index 00000000..7ddc98dc --- /dev/null +++ b/src/benchmark/index.html @@ -0,0 +1,69 @@ + + + + + + Scratch VM Benchmark + + + + +

Scratch VM Benchmark

+
+
+ + 0 / 0 +
+
+
+ + ... +
+
+ + ... +
+
+ + ... +
+
+ +
+ + + + + + + +
FrameSelf TimeTotal TimeExecutions
+ + + + + + + + + + +
opcodeSelf TimeTotal TimeExecutions
+
+ +
+ + + + + + + + + + + diff --git a/webpack.config.js b/webpack.config.js index f2d4c880..6b32c6d8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -130,5 +130,68 @@ module.exports = [ from: 'src/playground' }]) ]) + }), + // Benchmark + defaultsDeep({}, base, { + target: 'web', + entry: { + 'scratch-vm': './src/index.js', + 'vendor': [ + // FPS counter + 'stats.js/build/stats.min.js', + // Syntax highlighter + 'highlightjs/highlight.pack.min.js', + // Scratch Blocks + 'scratch-blocks/dist/vertical.js', + // Audio + 'scratch-audio', + // Renderer + 'scratch-render' + ] + }, + output: { + path: path.resolve(__dirname, 'benchmark'), + filename: '[name].js' + }, + module: { + rules: base.module.rules.concat([ + { + test: require.resolve('./src/index.js'), + loader: 'expose-loader?VirtualMachine' + }, + { + test: require.resolve('stats.js/build/stats.min.js'), + loader: 'script-loader' + }, + { + test: require.resolve('highlightjs/highlight.pack.min.js'), + loader: 'script-loader' + }, + { + test: require.resolve('scratch-blocks/dist/vertical.js'), + loader: 'expose-loader?Blockly' + }, + { + test: require.resolve('scratch-audio'), + loader: 'expose-loader?AudioEngine' + }, + { + test: require.resolve('scratch-render'), + loader: 'expose-loader?RenderWebGL' + } + ]) + }, + plugins: base.plugins.concat([ + new CopyWebpackPlugin([{ + from: 'node_modules/scratch-blocks/media', + to: 'media' + }, { + from: 'node_modules/highlightjs/styles/zenburn.css' + }, { + from: 'node_modules/scratch-storage/dist/web' + }, { + from: 'src/benchmark' + }]) + ]) }) ]; From a7314d6f0d6e9078a26051ccefe58ff814bb67de Mon Sep 17 00:00:00 2001 From: Boaz Sender Date: Thu, 16 Nov 2017 15:43:51 -0500 Subject: [PATCH 625/721] adds ui styling and context to benchmark prototype --- src/benchmark/benchmark.css | 54 +++++++++++++++++++++++++++++++++++-- src/benchmark/benchmark.js | 10 ++++++- src/benchmark/index.html | 26 +++++++++++++----- 3 files changed, 80 insertions(+), 10 deletions(-) diff --git a/src/benchmark/benchmark.css b/src/benchmark/benchmark.css index e843b5e9..07068f59 100644 --- a/src/benchmark/benchmark.css +++ b/src/benchmark/benchmark.css @@ -1,7 +1,57 @@ +body { + font-family: monospace; +} +p { + max-width: 400px; +} @media (min-width: 960px) { .profile-tables { + top: 0px; position: absolute; - top: 0; - left: 520px; + left: 450px; } } +#scratch-stage { + border: 5px solid black; + display: block; + width: 400px; + height: 300px; +} +.loading label, .profile-count label{ + width: 15em; + display: inline-block; +} +.profile-tables table { + margin: 30px 0 30px 0px; +} +.profile-tables th { + border-bottom: 1px solid #333; + text-align: center; +} +.profile-tables th:first-child { + width: 215px; +} +.profile-tables th, .profile-tables td { + min-width: 85px; + border-bottom: 1px solid #ccc; + border-spacing: 0; + border-collapse: collapse; + padding: 5px; +} +.profile-tables td:not(:first-child) { + text-align: center; +} +.profile-tables img{ + margin: 0 auto; + display: block; + clear: both; + width: 20%; +} + +.slow { + background-color: #ffa1a1; +} +.profiler-count-running { + height: 4em; + background-color: #dddddd; +} diff --git a/src/benchmark/benchmark.js b/src/benchmark/benchmark.js index d8540404..3fd1bc2a 100644 --- a/src/benchmark/benchmark.js +++ b/src/benchmark/benchmark.js @@ -5,10 +5,18 @@ const PROJECT_SERVER = 'https://cdn.projects.scratch.mit.edu/'; const SLOW = .1; +const projectInput = document.querySelector('input'); + +document.querySelector('.run') + .addEventListener('click', () => { + window.location.hash = projectInput.value; + location.reload(); + }, false); + const loadProject = function () { let id = location.hash.substring(1); if (id.length < 1 || !isFinite(id)) { - id = '119615668'; + id = projectInput.value; } Scratch.vm.downloadProjectId(id); }; diff --git a/src/benchmark/index.html b/src/benchmark/index.html index 7ddc98dc..9e6b885a 100644 --- a/src/benchmark/index.html +++ b/src/benchmark/index.html @@ -4,16 +4,28 @@ Scratch VM Benchmark - -

Scratch VM Benchmark

-
+

Scratch VM Benchmark

+

+ Welcome to the scratch-vm benchmark. This tool helps you profile a scratch + project. When you load the page, it: +

    +
  1. loads the default project +
  2. runs the project for 4 seconds to warm up +
  3. profiles for 6 seconds +
  4. stops and reports +
+

+ + +

+ Try a different project, like `130041250` +

+ +
+
0 / 0 From aa0acc62e4ba9c379af496f6ae7abadbf6bebe85 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Mon, 20 Nov 2017 17:45:55 -0500 Subject: [PATCH 626/721] Add benchmark to ignore files --- .eslintignore | 1 + .gitignore | 1 + 2 files changed, 2 insertions(+) diff --git a/.eslintignore b/.eslintignore index 8af9aff6..f571fb2a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,3 +2,4 @@ coverage/* dist/* node_modules/* playground/* +benchmark/* diff --git a/.gitignore b/.gitignore index 2f382587..97bbbfa6 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ npm-* # Build /dist /playground +/benchmark From 5fbfecb1a9ec641834a149a47161d8a62659de53 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 21 Nov 2017 10:36:28 -0500 Subject: [PATCH 627/721] Note player (#806) * Move drums into their own folder * Load instrument samples * Play notes * Concurrency limit is shared across drums and instruments * Increase MIDI note range * Set instrument directly on musicState object * JSDoc * Clean up the play note functions and add comments * Cleanup * Check for audioEngine in playDrumNum * JSDoc and comments * Round the instrument number * Fix unit test for set instrument * Comment fixes * Nit (condense onto single line) --- .../assets/{ => drums}/1-snare.mp3 | Bin .../assets/{ => drums}/10-wood-block.mp3 | Bin .../assets/{ => drums}/11-cowbell.mp3 | Bin .../assets/{ => drums}/12-triangle.mp3 | Bin .../assets/{ => drums}/13-bongo.mp3 | Bin .../assets/{ => drums}/14-conga.mp3 | Bin .../assets/{ => drums}/15-cabasa.mp3 | Bin .../assets/{ => drums}/16-guiro.mp3 | Bin .../assets/{ => drums}/17-vibraslap.mp3 | Bin .../assets/{ => drums}/18-cuica.mp3 | Bin .../assets/{ => drums}/2-bass-drum.mp3 | Bin .../assets/{ => drums}/3-side-stick.mp3 | Bin .../assets/{ => drums}/4-crash-cymbal.mp3 | Bin .../assets/{ => drums}/5-open-hi-hat.mp3 | Bin .../assets/{ => drums}/6-closed-hi-hat.mp3 | Bin .../assets/{ => drums}/7-tambourine.mp3 | Bin .../assets/{ => drums}/8-hand-clap.mp3 | Bin .../assets/{ => drums}/9-claves.mp3 | Bin .../assets/instruments/1-piano/108.mp3 | Bin 0 -> 9852 bytes .../assets/instruments/1-piano/24.mp3 | Bin 0 -> 75617 bytes .../assets/instruments/1-piano/36.mp3 | Bin 0 -> 80416 bytes .../assets/instruments/1-piano/48.mp3 | Bin 0 -> 58811 bytes .../assets/instruments/1-piano/60.mp3 | Bin 0 -> 57227 bytes .../assets/instruments/1-piano/72.mp3 | Bin 0 -> 46418 bytes .../assets/instruments/1-piano/84.mp3 | Bin 0 -> 30729 bytes .../assets/instruments/1-piano/96.mp3 | Bin 0 -> 25596 bytes .../assets/instruments/10-clarinet/48.mp3 | Bin 0 -> 29950 bytes .../assets/instruments/10-clarinet/60.mp3 | Bin 0 -> 33369 bytes .../assets/instruments/11-saxophone/36.mp3 | Bin 0 -> 24649 bytes .../assets/instruments/11-saxophone/60.mp3 | Bin 0 -> 24649 bytes .../assets/instruments/11-saxophone/84.mp3 | Bin 0 -> 24649 bytes .../assets/instruments/12-flute/60.mp3 | Bin 0 -> 45669 bytes .../assets/instruments/12-flute/72.mp3 | Bin 0 -> 33812 bytes .../assets/instruments/13-wooden-flute/60.mp3 | Bin 0 -> 34383 bytes .../assets/instruments/13-wooden-flute/72.mp3 | Bin 0 -> 36776 bytes .../assets/instruments/14-bassoon/36.mp3 | Bin 0 -> 28433 bytes .../assets/instruments/14-bassoon/48.mp3 | Bin 0 -> 27567 bytes .../assets/instruments/14-bassoon/60.mp3 | Bin 0 -> 28049 bytes .../assets/instruments/15-choir/48.mp3 | Bin 0 -> 32402 bytes .../assets/instruments/15-choir/60.mp3 | Bin 0 -> 31410 bytes .../assets/instruments/15-choir/72.mp3 | Bin 0 -> 29022 bytes .../assets/instruments/16-vibraphone/60.mp3 | Bin 0 -> 22265 bytes .../assets/instruments/16-vibraphone/72.mp3 | Bin 0 -> 23547 bytes .../assets/instruments/17-music-box/60.mp3 | Bin 0 -> 30465 bytes .../assets/instruments/18-steel-drum/60.mp3 | Bin 0 -> 28015 bytes .../assets/instruments/19-marimba/60.mp3 | Bin 0 -> 5229 bytes .../instruments/2-electric-piano/60.mp3 | Bin 0 -> 23858 bytes .../assets/instruments/20-synth-lead/60.mp3 | Bin 0 -> 60288 bytes .../assets/instruments/21-synth-pad/60.mp3 | Bin 0 -> 33916 bytes .../assets/instruments/3-organ/60.mp3 | Bin 0 -> 26939 bytes .../assets/instruments/4-guitar/60.mp3 | Bin 0 -> 26857 bytes .../instruments/5-electric-guitar/60.mp3 | Bin 0 -> 24649 bytes .../assets/instruments/6-bass/36.mp3 | Bin 0 -> 19738 bytes .../assets/instruments/6-bass/48.mp3 | Bin 0 -> 23175 bytes .../assets/instruments/7-pizzicato/60.mp3 | Bin 0 -> 15102 bytes .../assets/instruments/8-cello/36.mp3 | Bin 0 -> 25512 bytes .../assets/instruments/8-cello/48.mp3 | Bin 0 -> 36240 bytes .../assets/instruments/8-cello/60.mp3 | Bin 0 -> 28848 bytes .../assets/instruments/9-trombone/36.mp3 | Bin 0 -> 28495 bytes .../assets/instruments/9-trombone/48.mp3 | Bin 0 -> 28545 bytes .../assets/instruments/9-trombone/60.mp3 | Bin 0 -> 27147 bytes src/extensions/scratch3_music/index.js | 247 ++++++++++++++---- test/unit/extension_music.js | 26 +- 63 files changed, 210 insertions(+), 63 deletions(-) rename src/extensions/scratch3_music/assets/{ => drums}/1-snare.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/10-wood-block.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/11-cowbell.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/12-triangle.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/13-bongo.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/14-conga.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/15-cabasa.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/16-guiro.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/17-vibraslap.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/18-cuica.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/2-bass-drum.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/3-side-stick.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/4-crash-cymbal.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/5-open-hi-hat.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/6-closed-hi-hat.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/7-tambourine.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/8-hand-clap.mp3 (100%) rename src/extensions/scratch3_music/assets/{ => drums}/9-claves.mp3 (100%) create mode 100644 src/extensions/scratch3_music/assets/instruments/1-piano/108.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/1-piano/24.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/1-piano/36.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/1-piano/48.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/1-piano/60.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/1-piano/72.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/1-piano/84.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/1-piano/96.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/10-clarinet/48.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/10-clarinet/60.mp3 create mode 100755 src/extensions/scratch3_music/assets/instruments/11-saxophone/36.mp3 create mode 100755 src/extensions/scratch3_music/assets/instruments/11-saxophone/60.mp3 create mode 100755 src/extensions/scratch3_music/assets/instruments/11-saxophone/84.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/12-flute/60.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/12-flute/72.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/13-wooden-flute/60.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/13-wooden-flute/72.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/14-bassoon/36.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/14-bassoon/48.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/14-bassoon/60.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/15-choir/48.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/15-choir/60.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/15-choir/72.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/16-vibraphone/60.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/16-vibraphone/72.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/17-music-box/60.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/18-steel-drum/60.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/19-marimba/60.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/2-electric-piano/60.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/20-synth-lead/60.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/21-synth-pad/60.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/3-organ/60.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/4-guitar/60.mp3 create mode 100755 src/extensions/scratch3_music/assets/instruments/5-electric-guitar/60.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/6-bass/36.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/6-bass/48.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/7-pizzicato/60.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/8-cello/36.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/8-cello/48.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/8-cello/60.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/9-trombone/36.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/9-trombone/48.mp3 create mode 100644 src/extensions/scratch3_music/assets/instruments/9-trombone/60.mp3 diff --git a/src/extensions/scratch3_music/assets/1-snare.mp3 b/src/extensions/scratch3_music/assets/drums/1-snare.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/1-snare.mp3 rename to src/extensions/scratch3_music/assets/drums/1-snare.mp3 diff --git a/src/extensions/scratch3_music/assets/10-wood-block.mp3 b/src/extensions/scratch3_music/assets/drums/10-wood-block.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/10-wood-block.mp3 rename to src/extensions/scratch3_music/assets/drums/10-wood-block.mp3 diff --git a/src/extensions/scratch3_music/assets/11-cowbell.mp3 b/src/extensions/scratch3_music/assets/drums/11-cowbell.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/11-cowbell.mp3 rename to src/extensions/scratch3_music/assets/drums/11-cowbell.mp3 diff --git a/src/extensions/scratch3_music/assets/12-triangle.mp3 b/src/extensions/scratch3_music/assets/drums/12-triangle.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/12-triangle.mp3 rename to src/extensions/scratch3_music/assets/drums/12-triangle.mp3 diff --git a/src/extensions/scratch3_music/assets/13-bongo.mp3 b/src/extensions/scratch3_music/assets/drums/13-bongo.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/13-bongo.mp3 rename to src/extensions/scratch3_music/assets/drums/13-bongo.mp3 diff --git a/src/extensions/scratch3_music/assets/14-conga.mp3 b/src/extensions/scratch3_music/assets/drums/14-conga.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/14-conga.mp3 rename to src/extensions/scratch3_music/assets/drums/14-conga.mp3 diff --git a/src/extensions/scratch3_music/assets/15-cabasa.mp3 b/src/extensions/scratch3_music/assets/drums/15-cabasa.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/15-cabasa.mp3 rename to src/extensions/scratch3_music/assets/drums/15-cabasa.mp3 diff --git a/src/extensions/scratch3_music/assets/16-guiro.mp3 b/src/extensions/scratch3_music/assets/drums/16-guiro.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/16-guiro.mp3 rename to src/extensions/scratch3_music/assets/drums/16-guiro.mp3 diff --git a/src/extensions/scratch3_music/assets/17-vibraslap.mp3 b/src/extensions/scratch3_music/assets/drums/17-vibraslap.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/17-vibraslap.mp3 rename to src/extensions/scratch3_music/assets/drums/17-vibraslap.mp3 diff --git a/src/extensions/scratch3_music/assets/18-cuica.mp3 b/src/extensions/scratch3_music/assets/drums/18-cuica.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/18-cuica.mp3 rename to src/extensions/scratch3_music/assets/drums/18-cuica.mp3 diff --git a/src/extensions/scratch3_music/assets/2-bass-drum.mp3 b/src/extensions/scratch3_music/assets/drums/2-bass-drum.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/2-bass-drum.mp3 rename to src/extensions/scratch3_music/assets/drums/2-bass-drum.mp3 diff --git a/src/extensions/scratch3_music/assets/3-side-stick.mp3 b/src/extensions/scratch3_music/assets/drums/3-side-stick.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/3-side-stick.mp3 rename to src/extensions/scratch3_music/assets/drums/3-side-stick.mp3 diff --git a/src/extensions/scratch3_music/assets/4-crash-cymbal.mp3 b/src/extensions/scratch3_music/assets/drums/4-crash-cymbal.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/4-crash-cymbal.mp3 rename to src/extensions/scratch3_music/assets/drums/4-crash-cymbal.mp3 diff --git a/src/extensions/scratch3_music/assets/5-open-hi-hat.mp3 b/src/extensions/scratch3_music/assets/drums/5-open-hi-hat.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/5-open-hi-hat.mp3 rename to src/extensions/scratch3_music/assets/drums/5-open-hi-hat.mp3 diff --git a/src/extensions/scratch3_music/assets/6-closed-hi-hat.mp3 b/src/extensions/scratch3_music/assets/drums/6-closed-hi-hat.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/6-closed-hi-hat.mp3 rename to src/extensions/scratch3_music/assets/drums/6-closed-hi-hat.mp3 diff --git a/src/extensions/scratch3_music/assets/7-tambourine.mp3 b/src/extensions/scratch3_music/assets/drums/7-tambourine.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/7-tambourine.mp3 rename to src/extensions/scratch3_music/assets/drums/7-tambourine.mp3 diff --git a/src/extensions/scratch3_music/assets/8-hand-clap.mp3 b/src/extensions/scratch3_music/assets/drums/8-hand-clap.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/8-hand-clap.mp3 rename to src/extensions/scratch3_music/assets/drums/8-hand-clap.mp3 diff --git a/src/extensions/scratch3_music/assets/9-claves.mp3 b/src/extensions/scratch3_music/assets/drums/9-claves.mp3 similarity index 100% rename from src/extensions/scratch3_music/assets/9-claves.mp3 rename to src/extensions/scratch3_music/assets/drums/9-claves.mp3 diff --git a/src/extensions/scratch3_music/assets/instruments/1-piano/108.mp3 b/src/extensions/scratch3_music/assets/instruments/1-piano/108.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..a7c0507397443cc7ac9f94457f83917042def883 GIT binary patch literal 9852 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

xrN3k`sEPW|ast~*lA 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 D#Cdf6 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/1-piano/60.mp3 b/src/extensions/scratch3_music/assets/instruments/1-piano/60.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..adb7052b715745b0f68533236af0d804b127ed74 GIT binary patch literal 57227 zcmd@5^;eYN_XZBn(A^Cp3r#?H~%-ODE+=-G>xh^SX_iK*$C zxdla~`Zw|i4;w;H4FAdRP+UT8o(7zoS(rj9|+Th4CmVuMrsGJlR3vcp_a_G9LE{)ocBv zOVA07gHoDgq}DeWSQO6xHdvLNvS_29YG-(>Og3cIoyZMbAHT zh%xi9-j`2Cn~HJBeBrwJG+#cjr6}_gg!u$LI40QDq{#KC_He{@1lCHd79mFt9mY5( zO+YBIM=9^H+U5!|&~wt^DKh`@8pEddR%oRp$H*Do5TUrKf1-EGD@9RFTWsz$g#@hLY!LovO#>#^rFvmX!92{*K zOeikX1oI`?E237g*bl6DHmc=O)wWN0cGS_H=o`IJN~A8QjFat$ zPn~)6m3h?0%n34%>st}*GE1Wwr-NgexIFnc{mSt79_qF{aQh<>$ z{ShLD@v_#hH_xAnG7YjQHD6AhF(i@_OR~M74Y)qW#v6g)s8+OEFz(Z0j;1X{iexys z*yBhP#^cEJrue8|JuVKhx~pCb&%QPyN$hyn;q_GFb(+RgSO|;;i1luOv);ChiVW2H zQYZtPj-?w(L~qkIs1R4hptiUhC7@Un)8pmMV&B%vu41RRWQ9i7hJLN z`g&4dw#7}cnK`iyzfL!&y~8@JK&cY7DV-zRwDEp3Mz1-gFec3VcQ_io+C2B=7!O47 z{c|p!5tNyVB)_PS8Y2R82a+}B$0 z1wZc3&7)GRiMzle2Hw4oU6(yuLr>I2jl|o0)4s-%R2R_`Kbn&}Uc6QJfCU{h`;?=t zMSm;YteoE*tKG<>(Kiqj9Dyk?SjG4-;*~7|JW96Dgq_>v=}b%Bg-~VU;>ysr8+(ZbgV%Xw&mqt{pENpo>>Rd!f!k z8B5>KUEif;!gp9n0Zh@Id)z}&fQ{diih(4?tCMYXsxB?bsur6W{3pAeXt(%05tHCg z1ryX=h>U5{E5iz`nyr9zV#bHhJ+7$tgD2R}uj@b9H-otriiC;Pz)Hs9LKJ1veGaT< zl=Ix|H;Y0x`6o)%MPhTD4a7?9V_{55vg09#<*h)R64J*zaD4-{NZGJ{XloP$GLOOQeGghP<(Rh4FvoLH;IdtN{Ue30(7Fk8DbgVt9N@x4;}_lQ^HB{&c%|k z{VA1OR`mrpGL>DO`w@M5ZuC8=6krn3_fooUK&H~TaP^H|@W`2Q+SfFD z_SVwpQ6b%@U8|lJnYjd2voMJ&P~)$bE5BCaG~?;efa}?Ij`jzXqn-|%2V&muK5&|= z)d73B`=(pBu(0L`lKDfxlt{zC$}0L66N^?zxZLJgt_j19Fr8v*unV4$-PUI0yZw3- z#9?#8Rz~jQ;VmKl=RqOPkp?u&!7AP_5_Z)~fv3{8Il8zsaV? zr~y-8CG>!IoFidv1kQh?f|1&Vb&neDs&{>;U4mxXPC+1%?RacLI!OW;fw-^{kp@;) z#0NH<7%Wb*i5*L8n)E~+vaemsjU_Y`^N4t#a9z@;%%L~&Pv#CMVvfn;Gz9rSA&%-V zF=?%PWG|Tydi}aje$A`g8vVAmIvW7uyiKk~ zm3HcDUB*NPukr_v?n$KwC#Tkl>AG|6zM^(<13=KPUOQob`!l`abRP>+nQM-e_u@NS z`J>=JxcQv>eC+CZS)F8(bKc73!xispNolQ!V+=Fg>A1|EjP-%X0K``oUE1h)TSO_0 zN|i_~UU|?Ktbo_hHawKt1V$ui;L68H$49)dwf*5>v{SJwMXh6CXH<(cRiQ8vO?C6_sU=1p;A+qZ4HaFf^$s zg-<|&HOV>o_nArv1~Tz zK}_51*X6%8>6ntRAnS_b^VSWcP?FXVKjY>!YLP(5Al|DO2UjX$a1_fVs;W8K_RmgVF@iDR8e>6&!(bhRk%iuaZo6~l9N8k_y~gD7pq_Wqr3xGg%z63!PaJ@aTpPda*g=Bu<;v(! z?FnStFe?a@@Tzdg=^IPDS(@_+vWgT%UFxTc7+i79ag#`Taa2;)4&$v`-N-`yiQ@0n zp=q8gJ|bjgHPa>5GEJs)-i=$ASz_he7TSwn+5LG06o%YQnfVGXErGRevp7ZpWAgc* z{gSxWiRe0O?7qu#4*ED*zam0S$i@N2d?2$=rzzOPwCH`F3!1`qo3V|mPs&`1a(#-Z zQKy^u!Y;Mw(E0_Fv{%XqMbmaEb2|hrrPJUD3XGzoHH1&O$69@{{(I}@jBPdAcoycQ zqilV_&8XKnPOBBDlEQYs3!=uIGBQOF=BI@*WOE6fEwza23I!gv(lVx9D>=pwTSxpiI`A0gi$G*dpjMR7Cf|#t8=>e8Tc2--VM=9oG!3W=ve9jh)mFx7^Z@NBeiU6BH zqe}{j$Uv_*=mynCN66Hooy6oE$y-+SeD7jtPqjN3Zgq9~^Rwjuw)Bfty zu%VIXFSq(`+i}xpA|+n7NQq#gm=-|bd>TFvlY6BFNml7jT}NH6dAac`<5juxcs;4e zEn&>_0ai2rPOCVI0D%RXLEMnbdzwuN;dYGy6wRM@vF;exgL)B{nWD(T9iH| z1cA@SODSETS{v4eGuLX3xiK%DoS9C7j5o4bkcJ@0XB3~DZ3COJzU`zNg<}JpN0bg- z5KltG=b%krlWf0EVc&kmFTL_?!v?l6)y9a}IM&3_^vtrTOomQoH?31uVAX5b zU+#z)Aj!pU@UR~W>Ve7dF9q(au2pRh+GwS_5AwKpiXZ+(lGF?`)akyEQ zZIcsOw%!ndE1%P-=;?D|R^HH)U^R@XQ(u`DyRYrSEe5$7Dxw6ow8qlm(rfND0qf>` zsJp<;o))Fxk!Sl4sDswJ^S7Ek0EmxfjZ6Q()fEX7U{_AnyBXBZZa~Q_b{*qTq7j9G zt%sVH8Wa3bKW$9u7(EM)!eMafpk*0BhCC)=;KS0WPkhQ=!tQ8Re?}|+pr2xUsm^Q( z(*sJOKoz%xTYtor)sD%t7?M*4ch5h81(%hnxT!R;?OfB+SdP#(Mr8uqKyT%N@WV>; z^tgm<5t4iLiWEeNf*ZBS+u;>>Nt*dBo_Mg9S4Cz4cRD>S`~wZWM~)9mWurohTt~~# zb2h0#nncb)8Vf|FHuq}_fx=IsR5c%v8vIGIEf8)#5Z z(3coPM+!^smYuBzhHtBgT%A9hFX`jxA`rGb66#j)Tx@{y~;B z#XvpGik(GfBh_~R=MBdIPt3HY1^cWNSXmFfFn!T{ zPQp~e8p2G}n>dtqf=LCrWQyeJt=&Po@{&kO;NKJH*6JL;tlrr&$ET z{(DodB5X_a4X0dfn$J=kU#fjh*NoOX{>4-l|k|7VJnq(-WLwi%oU}yGHq~@ zZwi%DQQ$*bZyA*GhXBC3<%D%MCnJXY)Ez4)ed z0C5(>p=kP(cA#_~4y=EB@^gh13?h^*ZNMu*nXfL7u6{O9c{Quod4^Yjm)FftKHt^tgUkX((3c z>7taD3YBzUdA5At2;NylC#)g~;2;qD6%Gi0*a);0)#N)$At>maJF&YPqnx7ly9iDG ze|AL#kP3iMN)6bR=!F*XmeS%rjJpuj{s>w}jQdfq?Cxtsk|J?#rqDNOsRhRG+yX~5 z&JV2H3p{LmpI@X}#8H{@v!9ysyP)1TKLJD~w`tknD>#TbkBWH+5>;98{Gl#cY^T7Q zW{;DL`@n$MRd9hS7EhJWen`@-idYUQ!p=(fYs1fGPF-^idZw@1Y)p^fBu?6Jsej_Y zEuo)avUQo+>zcyp_w%W9s_rk1jN47%P`aC~H%^1~MB)L(ro?rujLZ_wJ*hb0*=Ow{ z_x4*(U8N6*6r>yX3@hJxjHB%MApx@mYcxg(FNU@&)lruyu_%s|H3`u~`J)i^g3uJA zzGpJKFPmiCXBbj@b`w-Jf^>ExfC0;Y|(<9hIvM8H#Q z3BAvr(RvYNm7sK&$1Z_**_}#~#ePP~u`veozpmfN#X65Q{V0WK3eOgABmi`_Oc&w} zX+{7(1rooGh5oTr9OTLvR-FlBnj6rC@)C{XVLb91!ZW&sY;gQ{22mo378*nvcB@NA zshuwS+AcoMIfVMNISEzUh*XptgJAXDJ0P&J>UdP(o-jj+ACK@ad0h+;k`xOI7 z72))7uA!hyWPqy+QhgX_#TeDnwxAD_gnHp1y(*~96>`Y6;}gQ=q3_=5FYQY(7pgYh z_1GTGF&@7-xZ2NKep&&U#kLp6_b#2%Jd>3Pnp-U#05tV!pj+q!XkQ*)V=+)9fip+n z4}BrR)TV+9oeD0228G>dq7DccF&Ji#Syb`5uG39L%u<`b2z|DCGVG{q!ddgN+N?O~ z9Inxx+Aiyml%;rKQEtS;_T|qI-;^=iX#-02KrI+H<82a?)`oVzS%YH?Q^Dsj|aPq?QrkVk&Grkpl)4aQHkP+s0APk7J8Z_?`lTJi$iO)kRAG6}jfu zC}v%nwD9~CvSUhT)TbuDR1a(uzXvZ?FQ1;hL7_uF{345HB@No)J`G^DK1qo-nthG_ z8{`Rzia^@${3wb?#scI$xQewKq9;q65&a=U#f}r7hz}FWm4xf-p_Chq{?BC1>V)p~ z8=2`G1b~3ABs!~>?&II`?)bDRrXV^qary+GVRWk|dyRmCnY2OwvE_mMrGWX{JS_{7 zh@=^ng=gMl-@8}Z-JGz##iP;3-^UqY&M-XwcovY(Ws^{sq(TYV;cDN5v_G*H}#0YQ7Qxiw@J{~+IQAwT9y}v{OEnlDL zDTAw0)yrajcYzL>sOs8oGrE7J(J6%V3vom1>4>=7&wT^kEm=x&wY(_e$f=Udxzd_% z`ts%TCni-Nl4|6vmBGt=`QJll{+Sd5Bx*kCv!8&{Mi79+ZJ#iyeqfF)x8Jm@Fh`&tph}OF-S;H^HbtAI>ItOG7l+FiiBw5NE zwS}x5bQ9KQa_lNRJwAGhMw=Q0yn|^}#Mef|BK|)U#H{lFQY(kL>RzOPK-Ha zW~UgIgiZbY9<8KLX&927$#sf{TK1dk-{yUP^BxEg_^u5v3R9+9soz< zH1smgaFs@_@G@Do_Eo|G5!*u*3 zzjt>^ta<bpK(DA8i8wepphaXmMR?1M^_B!2E0>dgzon4bqSzI}{sppl_iih6} zwdZG_wd+l;-zR(8|IG=zjkRUe{mP9B{3E=hBA4cMK6t4E5XzIxudznl>r>_`)=H?P z=G12=&lktnyxIRg9zwA8DIijNT-La(ds0s&F0}TP5lE2QDCNtacFIb1awJ4apmatR zmcJXm8IxD2PbF)VE75XY6;P)@3+WF@L3mQ6t9E_PGBkVRe{M8e{;tHAoN@ z4^QIef8{FC!v92Cc7Lb?<*HMJo`pAk+4D3Cdg<;2^@!3LI337-e=m9}K-j^pD2WT- z?^L$+FV;_#vVU#41vre&cC#dHI!xCLH<)F8V?-UB z18ERYL`;%o9}mPRcO3(@U3r~$Vn~IFQHUXw_yPnMw^y++_Y0)ER1Hy%U#__)l?qJk z`akC>%eX&IP9->IjmTYyj2>C^=uHV21VxQnwy?UdWmi`{%CDd;StZYNp=e(Aa_NvU zUsO%hz;OA%SOC$X>6!}l0J^{9Cm-BKKMV_!Zd($exEgy@vpLAB9A4pw@Z!W`dU9jg zxtU^od(O43=(=nQ=e=9>YrjCMCOLYhdv=sU$+dSHPdvXYGpt=6tF|Vr=*{ITvk{iO zz+$Fy^%NiDO>E7+z83$Ki9Wq8c&$5B$18|{1Cio}49>9V1aZ3ElZuNZ(J$va8`ZZ< zqI4GPtZ^)Y(1B_iNb+j_d_XNe4<1sUqddlEbncu4rHcP^Ri)OqlMxQkYah)n-EaO~ z<>!LI($_(Tf8x3SRpDDi-fUe8>ZA9+nd9W+j-Fq_0+WUoud6-Ko#6o1=&OP2d3Mxj zBi4tdvfC6wAFBX}3rx8p-0CbxCITK6l3_VU$jG(;?Z1m0u<# zSr0d2Wbiv*xs{36^E?Bmgo&*wQZrs8A)T*si5EW9=XYb$6A?hP7K|*`t{Bs0kpQQH zGFN!64w^rac^YNl#309U&cP)Y2z}H8(<@1($@qqef?xTL|B;FWDjR`&=c&qY3hIMj zFcptQQ_0tpr_`gK*F+YERCtgHxgW+qHn?%H6kb}1H$QW^WL(ge(|o4qM%hu*`H+>> z#}tS#L`aP{e)x+bN)LfK%c6jJgtJDRT9KzWrI5Ju$1@H}kWT$#i>rw1i)h&w|I8&) z0)_LvplPa?{k@S}4%O<7<)zT$WZcD;PaWN4@?~`oS3FP~lUbd$C~5vqm9K=`;z6g5 zzb?h(fvIoEq#-pY`V6jzxTkbm9}G(T^(-Eb={_Djq;^}m&>2vvxo1>~)r)>*Hii-C zg5x8yWGeCkE_Oyc|FA#e-^<+VjFjh~39;(_J|;P0;7O~v$&7tn)k83=)XRteD=IPC zFvmdVOK0@P$iWFFixh1!i+N;n#&;m5dXvD4a-#3$#0D;g=F1jKwlAqQFe>p7$;%HpFZrK0R{ph!vP_|z2FidhEbl@dfKke-tX^}`%w0nLV1;HeDDDTT}S|nj%fl&RCN@*vhlUSb^ucO_JOLs`r&-x$X zD$5C*my3Y{(@Rp~iIrRlWy9?G+_J~!HgAA(V-;3cHKP}U1(I!UlbNC;WE2zH@uN?R zqerIxp#W|VRn%heMAs$R9ig;^Gl_7<5NGJ=yXBa*M&hm?&&{K4OIc?zoTh`sl*=?# z^47v?XnH2cW9KT87%0|WrH(lIc%;5E=<7J}bU~x53RFr&1up+hMc(%m_taq^i7E>C z?wf{a;83rJUC6HCxFdBGg|&|zKvftoh$^rd#f9NmRioN2Kjz5(KB|rorXKNN z%kgA&+wqGACNG@4R5J#HI=Xc%4HVEMLQ}#ByfS?_aNYbiGODCG)$cA&Qe$2gP@`l zv4|++Xb~o$W2Y6v)F;L7(nhov@!-yd=wQl<;r4`iu($9pL;4jLG>kZj-RB;5nT>Xg zb$h8F@?AE|c|9H2(y8SC?tF9!H0R+y=*!!{Wy_%bvV-YCY~Aps7)dzzGLhw(UkRBy z0Wu2W45fcKct-F^k_j8b44sR`^C@naM~S|zvh9VP<3w+~Qa}isL*~CDe0SWkXu-6% zq1?}RW#uj(3nh}j$khn4Y`w2i zyN_G8Icjty&P|PQr!omXGueF3wQokoWWst60tu2hwt%bt$Z2;0P(n8Ylmd&xd5vtJ zx5tuKF(`>55<*?9K3#lF^MgN$Qa9FiFCT>a{hZ*vdGKLpquf@C)vLqrU;St6NF zN8N~u`|&}MuXKhW33k0vAk|bIeNOh<`{R}dM0{Az)gUIkI|n#(eOn(j<{O;?aQ1SP zOrTtAOi<(`$&lb(DCF-`!n@nrv5Qx%)p|C+lVL_h^I>L}TsvS)Jd#l0>Kc9Fo+<2! zgCBwpK4u&SfmldAy%K~qJ4pOYM4QMR6coc>C4q2EDSL_OCb)T>qCQU5Hvjz?2Zo3+ z>G`iQu&r>;CU-JL+&mtUK>{^dBg$!uMZ;0b=Qv2R4s z(;IY4WxQH~kYo#C8Ya5@x3CY!|9Li;c;|odOi)$^)j*&i01S6EXvWJx>=G(&l*)qO zidCi%0=FiTK;Kz&0*p`HE?47qBMVb_hkHI{?qxqO?W8A2v4WxV*V2sC*r;yu6QKY; z?c-zBu%QX-AfkfbwLm%6>;6+KG1$gsvO&C?EU!+W+*E=;wH0mFKl%lo_yq(g z4Cp^nemLU#D0H+1C)y6aIzE`h`>te2?h6|gjzjL(W$*2;?zHYy4B|O>Md)L1K;rjo zW2)6x{4ubptTO+4uWWn-u_&Ny;Na?PV-Ko|7yY%xi^c8JCWcK=r1x2D$_EuLqA?8H zjn50PgqBbxXp(vo?$%|4X!mSJ-;~RoCdQ-0R}94kyILD7b~~tiCkf}p31o{1Si{kG z{n;cA=}{SY;qMO(yWn6D_$pn0K?ubDV1=d#hZ zCKB+pI+OIgiRcm437&{Cl-lI>gp<`kpm+`LHli9(!?&n8=iRb;o`m@24>Z2Z^Z|G%=1m(>00eegUZXE=9eb$Hi(-4|>seaYkh-&wl9fs#Af(-#c@0P?5=`MPVLm zi#5_6)@BYP1V>`)*btIr?SCIg#nWU2M#Dc3!yvT|VPvl`wxE%WW%(%%U5i=}qSOi; zN|;>3d5Ui}uSUKXZb(jC?Gt&lR@tNXfojU1U%UV2hV+Zvm*T)zr@nQX{fwODeDzyTM&B+twUt?V}Ggjy~oW^N&|t~hh- zeJXL7ZR06Fu0@VnTh%f53cH2_^<9G+_1lhlrjKoRSsBUQjfzlDF7rtUt$TSs0zpMm{^yD^7CL zQYjvNI2d5we0|sO^9nCl*Q|*N9(qN7rd4N1fdC21wTPQoTjq3imD#D6*-oS>_{6H; zgFu8N5v=5Du&cHk65JC*(LlkWV-GRB3O<`rMy^wg4vcl^c&<>JT6GMC^BN}2#d^i& zNHe0}n8&xeY{Mf~S%b3c#M=yCZCgG3Kf|z zQ|_`{OOcmJO&>JFUKg`WxhCwH@EG^dyl@vyR;Pk5H|y^1UV4f0OyQS4m4-++zHDYH zn9DU`HeR5tJ_${1%`9-hT3n%s)ILtI5c6@*lU_A9Yj4?C2vnVK2^Dy`y}b!g{LLmK z{j(cCzZu48_?@-EeT5>oBu+|_qCw@e)OpNI!>vp_%S2#>ufvkM=i2F+M=fC7aj$Gj z1aTn%f(kk7f~eCy2pbWB0okdkCttqUn-@B$ZgRlnznh~>ci-0Yy;zHPF_3F`K~RE9 z!(CBScgPpei+!HiAizCMpN+0cGEEYUED`kVrf^u~(*}XzVdb6-xmfg;I>v-2xq}QG z(R@mvLt#83vJ{R_N)i94GB-|j+%>@$V4XTZGf1C%Ll9ihmjtnzb8Vbw~UlzPFx z#3?%KQ`rI8nQ9>`neJBgcN-g1#W^pgtDgWT^tNu|8l-pA)G$c^-N__#PbvsODpby{ zBWh^ZmIZVNus#qNeCkzlOdLcqO7?6O-&oLuc zsZ(8<%&~Ets0RgsnXI}@e@}Hn>9emKluzNFca@d-Rmw$o4My!xwk(`_pJ-`caQCP; z?CZ!6Yy;O|7mJUIYK)-ELJHLjwKKZiiz?i<82D^kY_`obbT$F3l4hr5nBaQDSjC4m zOt#}dwUe7+VEkMQb_a&NlN=H_xgc2AzZkT@V;Xe$mE<3+ksx?WtfKTzdYOPfY1UW42gfy~3#vj2B*UhxY z-)l4WxA@s7Vl>Ph_yv{7vK3CrTn0W3)izS&eHW5>*-^FXbX~I&H?_=1@9MRXAASoo z1@h*%wXD?mEoilmzbk7~L71THi4R1g>Q%%P_Mv%yVuNxvltClrSU*w~+YKrqt5adF zmv&va^`G^FII_m$ti`B3D3(KsI?q?|EHvwkc3jk`Obq;u)f|_d)#UkB$>+YddwlDD z8mQ5DW?!2D+~97a``0BZHJ`;-AR*$n;U~$1AW((e5{^^Wa&y2v2sj85z8tOwxIR}? zCg5>pR;dbRR=ccN)0LBbI0CX{1o=Tc)xTV}a$x(}6j9d^5NY>~(kVCFryqUeJH(l` z=Zc&9sSlr`66rsEriq?-n6}4~iB>|+~(TbnT{MLPy}RsQ|`ANi#>FTIC1E6kAf zye4JsldL4X35KS(VSLYRTWq=B4f*eiyuKFD)Mo*M$uHAImgsXumB@-^##J=mwy+K= z;Ja2tRac1?_?>dnv>&!(>wGjnZD-iqFHzs~>-$A}UVVB~-mllR#iQ@`rMGu8gte-d zM>ji2E<;-EcZ+Ll-Ocavvjg<^O|&)tw1ml2x-zXk`t~l%2MPkgf#%))f&6#;c`M{> zl)~asx1t#MfF?0dv(Ae*9b(cl81PlQe9N{^dZmEZMaAXb;}}51h#<}eyYk(^OrS0m zeL-|Wy5Ch8yvSE1R5PxE{TRlc zo2V0AoP0+=%{FZNa(I)nA1t7n`07g8 znyAJFGGXrDUVjrR*4_|~-bBA?CpT!UEy^RPpR(N(G@>w{h*kiR`OBNL(b7KDZ)ING zHt{c`z0*$EDP1{-BXtO7bZBax_4?e%a*)qhaI^=~)sxx0Ck|fV7wV$) z$eV0;fA(FgnayNrGTL4714&wiX3Lrwq3=qbn}dWIpV&otmf0^>`x6p8<%e%pt#N^l zQ!uP~oN+3wnc+(*4%rJjs@To^7|1;9dL#yYe305b2;?AAog&T#^=8%`L7;>ofNlhf z(`J)Aqu2hN;lyhz98bU0Zl7Ni%|7qHGNNC!2~7Ue9az5iE{YIx~FKgORbhwin-Pf6ez;4ZKmfQjKFIHPP7G2JU>g4)cZ~A2{(Fbf1 zW3kbXsX-vbN_uKu=1151r9JM1&!3}k>zm{(1nNyDsPM?jF;wSoH}mci-=r7)(M z8?i-p5B=5I$M@bB@w_CojHH9Z9ATT?Eln&Kgt(iwEH-WPO7FgOUvKLeypSAfAcUi&o zBvr{sE+A9cA+G;Bzqu1>d>#f1)9{SvW?7 zAO(Tet(KN9We`5*nIn?RvMiZyyaXOW>5yQY%)pIB2(FkVwz^p`H2kEqX|tFq&@I9I zUE!}wtr003+~psthSLQ?oR{Ma?dSbA7bPGe-f7D7ApgBi(XtwM*!xcj=(}z7Z&k;h z)Urt@XJ740Ly}?RIsy<2LT=6E=}RN(XZNJyAc(dqdFtErS)1D`#jIU<};cxc`uvvWu{+T?fcF4dJ zj;Ju}YMbm{!+!Z*!>WS7lQ(w*mNxy=#_H~SgyOOs7?8R#R*eO z2GtY6)u$!55OUf1VW(H%=NA}ZHR#Uh)TOlBP8W-TzbAc(`s)haYTL&N%WjJ{Hb_fn z*7ci;)%N>CxK#CU7+)EQ&R?agarH9Rm&s@#$)E=d?410CJM zGX%`>uNeJxB}B9^Clb7Qt@7xl*z>wiOZOm9AxPX_b2Xq0cBcUlDru}zYEM6aNZbsx zpZyKNjQSw<`+aWy&6HMM1JlefL*NacrizLCgS7rr(;6?62G2{Qlh?n1mqRzVJtnae z?;Db2_A^FEgdJ`;Jx-5grDmvh&47>>M)oI7n)DSPU`KH>=b|2 zS+SY$g1T*S=EbTsGIhyT{N{lb)b}lvN?qg2J)BaMQ&N@X_w~SiKxx=m;#W>Ax z^STFawoI42JF}+NvpyUsiX5`UyTqe!sklWDw&Y}Fq#M&D>RRkqLhUmeYUnO0fNcMNHjaJme?sC`tGb9?*7Jv8K7byni4!N>#N$VfYu zC+K-30t|vPuR~-~>%e)!oOCDUREu+D1vOGyiVy+Po$@3J^QQhIHGPNNdU1wa*w@Gv z53_KwrgY%_x8ux_&FbSBQFW6Gshkd8Zb^m&+##lalTv%1q^gGJb$$nnJX!Yit+g8Z zOj$CqecBVVO1xp!=%0Fq7Y+ zVa%F7-P+?-UGx39zImN)<0wZ8@Lv(~kKM=;9dL{TzrSlnR7<>t!3v6LJ{D z@^#lAKBn}p0s146_#YY3)k8_1MykDJZ7f0vs~F?gPvQLJr~EJdz6S-~sv6d8#Tpfu zzkZt#Y$rwHUN8G384_w-kjQ6vbpXJEtpt$$DN)Dx!#s>e`La3hZGux z8R1IVEP~8&_3tELEGJggYdY>Pg)itEZ4wN8 zyvH#2;Ro9$KuY!Qkb2n3`UZoe)MQqU_uuW)I~+0 z+#H7up-8_QWAtID9=G*qQaw38CpyhZ#Uu+410)0n>^l|DjEoU{6?N_T0qciFm8K+p z2#3QcrzXcnh0nQ}<%?>a;;*%jK1I{jUP8wdJ`(nRzH0S8e!r+aBNO|mx|ds@oG;i? zY`NF9I`4YPBiq^Ws59*B=r{VwKzC2MyYWP8ZY-VnvJdb;Iyy4`e{{b{2ES5L{U@n< z4r)?Ml?40UgFu8JnYaZ=)n~1vs1&QN;T^*vDTeoMo&IZ9Q{zU#pPz&gskq!`srva#(mB7k$&vx6?c^*zkz(C3TDf;wA*K9!v=<5aXYDVwWSHLftuLB>N&w z@}`eQ+{=3qs6ZrJz~meNVW`;gvMTA6K`X)5>|vqA^c(Z*aVebAw9VHRIStgu!;>PYodQEwSLxLg6W1Kl)95P6?Iq;0kvB_k@4UnZx#7HvN! zJ^IE#YUH2Tuanq$%10W0!dlPFU8Yhfg(0%XKdb4`?;jA@VrvT#fnU{2eSE!o_Ki^^ z-3>=GTLOJUt%ilcf&nQ0sbbHR-ZR>K5RSG~s<_x~HUSoxJ#{vgVBndx`$l6<$8A$( zj1*VOeR|V@NS#XA8<>nrn_Hb^tKv?Bn*OnQKEf_pM|^p!|LshPm5FiY@Re-ZYVAbq zZ+E@%y3$3G8<&eo&ir;KA@8J{wk>q$Z}08j@)q8WdpyS0*+Yrwo(i*J;R!svK{5;k zi15}?*|5ANb#;6chFyXIYV~4Skd(Q#1Hl-0u(=r`TWD~qlzD2Uz4H6D-i>%_-RxQB z)O~kij+M632mgAn*z%$NqT*1ob#6RVx}qP2^50bUIR3tUUxlOp)(A~L{iqB^->oV} zVc(MqMvxNU+tp*c<>ozEz_Wpy`z2M6nMZ>7gLDKG>Sk&v7AOrj7E9)>;+|CMBf-*o z=otezJ5{zpP4C^A+D}I}ySTsOD2R`GlKwaCUT9Vs*|8(HjZvUu8T*qKtFIaD2Rz)? z=j^=o^A_C=ohHkV(au5T{nqQ)`%=kxJCGHh>{|*vG91t-i1NnVBBI`e$@?v)B#uHd zk|FUF;p>-~;S_Pa;;n6y%8Ijy2XT#BJmH4uWejq`fYFlr&!C85r#u#Ljfy}!uVjjnLXa+H z>pG*%Wxj)XL)=!cHr1~liqpdt9@qIB&+1Jz-YxQpoaVz>Lul_npqfdkdXSs-wfO5* z%UgM`rz)mGT&&cKWLc!!g>QR>JZ=B{+1?OLD=AD|EHrZk>V#QymCB>bY9h|qCrfwx$E+^j$uunXx z{WfaLI9JLu7IW)mJ3ikvYjnq*J$S8c&sXby-nP~1EHDjwuZP`OQZ0`x2S9`ut!{A* z|MT_(a8=>GHyng-&Ho7yJwBQkq<|1!+2Q8eK`au1R}g}@5R38}MRiFwjxaIvcK)A2 z;u<~1^thfVFcQUk57e`?J-%+> z;c@O(ckg5H(&I^knYDC&VrV}E_MCfy7LE!)gqJg%eqAl4onA!$04vg8c#ydNJLmzp z=f(elFj~TJlbLWP-RMTi6fLJI=BbK?6>`fQE`T3hC)?pyBTY7lg|of3!*ThYiB|(v zKqqxHcc}wE)}zTOyi4s`x`+xVrp13x_kof^l>cnq{~xj+|rn zuV4LcD|T2nQkZo$!^_p&sFHy?&cT$l#|wSNFMRpL*J^k6_6xtECrSd-F2&FH#G^?# z`h^a7kt5Iw=WhMq3r0o4o6(>pg`hzM$;u|IUy4ZBUVYz40_+hnp ziw5mX3&Rckj00y=OR|mMj(CRhwt`u1n1+*NEB#yXpIRMJ#Jbo0tFCP~+wW{fswYlr zFZAftaFi(%WL8H@#56$L6>Gh&stUK|5q^RB(L;2x#l#duui>sFt9Lx_0BN_WJbxr; z*r=V%UtyAK(rw}6gfg)bMArd+RC$IzO5PwNmWUR+-Og9q98|Vbl?g{8U3DS**crti z*OpdzagF3pP0cTs%dF(uDV`b?9^Iy})U;+cSHO=3l$Oc=L!uU&5rK7_zb~Hf zt+=teO3zS$9;VdF1G@%BvrR6Z0YvI2nTG(RK-{g?>&m?ud%}0yTeLf8WWUQS{jS^0 zh&N4W5kLAdecm8e$85QNTeCITF=!O%(#E-Vq?KO@7drZ)f?TwW);tPWV&FH9HiEF2gn%Ymfehu>`3OpQ-2)d{5pc_f;B-(e z9%zOLWX7~$EeztdK^F(q_XJg`GZ*4cJJ80M@V!kD*m8`yP&A zfTmAWHQetg!%kR5tk9WoO14jd_^qwPmeq$V<_$_0l2mtsjp<1ptGWzQe}{ZiC2R+~b!vO@j`- z#k#dEkwJ}IN0Ck0p(0zV9F?p&-&{*Kf;YEqU!|!6QvyrwK zNVLAG<=;R)4W0gEJa@>a8Qlz8;N^ObpX@eOphY1OCD4+Qr9c7V;W?DxvUz>T`TY$; z#l2pWPw|ABiwzQ~MvW4Jul|7oC-`Z#?OmTG3iCRHT()EsF(*9>LCa`0Pdai18si(3 zW@Te-cfIvg%n&C2q=BtuHPfaH4VpmEhoXND7Qp2x>W(D>vKw*@)mEBZ^rj?gaze># zJbX$`OpL_OQuHX=%I6OML5OSL&zlH|XP55Vm&Wj%){yJon& zm>F}9kDIi%Hnr2tK*HcO9I3{miR_y^D?DgPwh8j8^V}ht&j^hWO74|mZ%sleV)!22 zLABS|9gpV{fZieE*G(xaYy=XR}2ai^7fjc7rr&*rQ+{?vv~Ks zU7l7`<1Np~Qc?(u&5Mdf18Day>7uoeDF* z>pp*&+kVQ?xM4K&6{t@MiHovBm!s)}Cwn@YvvPGq`^wT65YWN6lmDyz_JDAcS1bd% z^}sY?9y28y3u5Y+ba(yru2wCk&@qvb2676Amz{ubrY?u-@Aj*@2y1CW@jU^`YxarA zVq*K)VY)hKHRF~vdybi|5&iw5)9I>jDRUw0A@F>$LX$dt)>N~FB1Ssc%m%xa|9A8e z%k;z8WNb4&c}^Y)3UZ`gtQmJM6N!XJR076N;*K2B*&;CQXCg`gpyJ*s~ znxixo9X)XJV_LYmBe~70X(3WsH!6R!b5ge3A6q;9pYMlz3thw1tx-un{eun+U{^k{F@qC2=eG z@h|gY9gvg74I?6okVtZOr4TY_brkyW0s<}=S2gRuvggGxkb%kYj^y+8I{a#Q z)V}3hvhLlKBmd;c%CGY+H>CwDc2-FDwT)h8^ii9a--w>atf`*~)!m^tB&R0xGn9{y z&#TVsc-bg;UdLd+pu)pRzv$yg#T|FLx7%-tXjHYebsY6ra07j?+fXP2@4_4PcRoil zC`^i@bWQGmme@YT+6VGe?*D#NcU1qBSseWE-g@5qk-ll)XmXa|PPgaYfc#J(r*hDa zRc9t$ONb@LA)s9~55B36s$Fh2ftPDy*h@};Ts?|5G2#vEvQ@__T9pO}|8{#64iO8? zMqE@Y>};VDO^r%xnqNXSDAk^CZjW)FUqE;TAF#*3)%M1nE&l(?9uSr|3uQjSx!!Pn zrJ6eMZ8pGn_E(ZB_9=gK;*_synw&80;z&90IKb|>nvZo!dh@8&qgTgHsqU%e!?vN< z)BLA;<&#onmYiQK5(fp9rwh28m^?&?DUXWy3K`rksKr8|=%j*lYi4`^Kr-2{+G7RD zxk)i~F^MfHnb%HOR!gg|46vv}ADNh+Zp09EFmni$bLVvk5FHbHkvu(qrpenCl_|=(`ql4#@ZDxamPmAFB+Yc@LG*gtEU8rVlU9 zp0kxCTW(?iNS5`}=33i>^lAiyUqB!P<656G*0H$LF2N%Kg3I5T_xD?WJx1F~gDfy4 zL?9gPxRK*)wd*lR@H+PH({DRZ_ahsz8=_q#ZjY8TC*k(2~5vD5sBDH=D8s z**{b>PPcBZCRs$c9Cz-vyp-AX?%UkQ*EKdwYFT&Aud^6%_N&IQq=3Dwc_y>n2Og|a zOn=&7JqBw3cAoCpy)HUCR9ebq_cSwqn86GslPyv3nX(?%CV$=%om2AdtWOB^8!7z7 z$HwGQ5Eb+0-{WQahNsU*q7W|p0s;vD7kvC*c35mKyMfbz=ES+_z5;hI``DtUtvoyW z=uxmj)*`S#_ufcg=@2pz9--%`n?1^<@WcKG|2qku${o3c*19b1#!AwgZDA7zm%LYL zb{EU1rO_w=DR}Ea%JJ$IEq^^Gg*}-~%Ue3V=wS(BK~N^C@T!s8+Q4>vm|x$ee>l`A zae*Mu9qEDpyJdM2luq+{(Q7q>;99drLGx%>Y2CNfAU4Ypf7&o@qvz<-?r7<@_9=iJ zI4=h!UdUo(^>M?RC$oo3Q1J46`I-(o{zv;VdkYCL({h^v_$+IJdsfIT<_@UO{kBpWMEGOCB5=KpoM7wobA_gs$78pCxJ!~B zx<}#ScwL=ORraXi*O5y6lam0ArSVnK+p*qF_NFrE@g|o+zMIUGFZOF`6=Mb|N^Kfu z0eJ6oJRY)yK_b8*mE12!7$441+_zU4ju#1Hi2uZq^e&0jQhU`Bmj!>SGV%z6r^Oo^ z&)M0k$}06bA{D%wxPeN>rT?DIYt&;@5~*?ZZ{_b+Opc$NX&f$v>m_ZD!UlZS$jq@A zukQl9QBo?DX`pG)OQ}ldabN z!V!!(GaVfaIQ*`_#CVj)fZI*~eH*SwYvdbh;Eq)tMGK$u*Gg5S^oo?2y3UTiwVdq0 zdMxZW!QB`{qq7TV>xhdcqZ}sX?T~JX!Ik+*H&;u* zdr<~zJg6?>8c&nOXk=W~T#xwmFhf$CJrLa{{Pu3DJLJgHt~HC1yZTn9hBA&`?4YDX zbkRW}+Gfnpc%hK9}kD*HF>8n ziID)$7kh*k3K0SD6XM{&saXPAti3B@K7G>Pl_+&9`0QO+Yj7c7DNSK1cz+(J>o%%g zf9ty~SItzNDZH8}@2$kgs##e*j1Ckb&bH!pvg%BH$k-q}U=22^fO%EiwCr_qS{o(f ztIRNcQlWSm?nm)ctZ!0fhdf{H`EYNK1|sAJZ%WD}BW`Eyka&Rs%;fzV6^wMg`d zASaq&R^O}G6=RY%|0-^2eVMnsEPscBog5}aKuP`rq+7Qi>Y8RMJi9*CV~z@CjH@o( z*-(u|jnrtF6qdGjRs8Et-hO1(mF*m<&yCB;2q~nq&KhuPkCA`GaUmn%8={s~Q7og71eTLn0~~{H zG%FsHPh&9pY*uJVc;*rIhTmEeXrm64!QypZV<8ku=nlz<#Q;*`pATh z6>)|M8L`G^k9q6alV0UsxqG2@*zr>iFKP*mz|0gw@w}^Nc-OXnb~6%4IvxRmzqM%q zO{w^WLMS143j6;>0?t=!f%ce|8k$jQns?R(**7non@ad-D??hUDO1cDUlqTF3Y*J_={v$EKZ!sV3#M5Sf)o*osogO zkQ9oO|Cyzm{nJq-7z^o#{@}~WChoME5DG2t>(Bn2(k7j6z|JSYXHi;AK4SO@n;MFS z76WVNG{TzFT(bG({peXsp3th{uVw-P3)$eu9HpsA%qh%lz`Z#o}YS*_l zAIIv@N5glc2s8~c7F+d1eSAkLS;J(VmJo&wSP73BJD_%`>sPW=XL4P(GMZpWTdWoz zeW4H;82>zrzE0F}=O@1nTyJkd=*?iw#eyRFF!oZro;5{}9`fmS1bsLDdCw{GAk8_Q4upoN@z14_`>d7Vgit2a z9H`w`70cDcwm9{9+zaBT}*<~S%Roh zJJkmN12dY;K))}eg)Uz1drBk-w%bi)HjR0KSYVWK^BB>Se`K->CJQ8%JGaT`5^oQ` zxJbgG+sdhshrsKM_eu%{8J~|mVhjsS%A>*SOObl)Ww_}N356rnl$3~LEl7P6$_LxG z!seSqYpi!8b&=LSBa1qvuh4uh5|1Vln2%cWL+&9P>~w)&30ieZaWzB zqoeJ9X_rf784XWNgm2pCc4n)sllN(+n@I79h)k_@gXEK9*{fNf@Z(1rc?AdS<1k-9 zzy{+d6vHXxPKONLgZuH%F=KFvRh8myTvU0VHY5e2Na!MHayyYtq!g|QHHgB@W%;dx zy!R+$@FMLE2=1QpL`(fz@>6X5Wo?`lnz^XT-EJz(*q-g4J{a9!sc~Q4W~3iJ8~9HD z>AL&)kp5a&3M04HAHH<1gNMAtO+$l*K_{gx;cGGrMOI{`treheuCb+(g^~z99N|W~ zDy#ME5S*!ytWcH3|3M*K&C8~rCJG^`CWKdcfz1uGhY2b2_@-6FNpK68SPgT&R%%BNWIIjRTi13o6FFuqC7FZNG7+mU61)WV>o(AP?XVIFlT9S2s0ZW6ezU$lkZcyl>BL)#kpc62cIj3zf z#_4VS6!k;1H z;!#!Xt^wse4c4ptJ`Z&iBIbq)%TPwI%#0E9;GI;hOg4t;`>{5XsVjL<(4Md3AAp)m+*M*5BaqYz%p zDs5ehI~^t*I0&Z6*N>~MxkRHYbf~HW7o!;zE9TQQEj0wkyJ0;Qv%Nv(!N2Ot0yMSS z*>h(6T4H^@?vzdsQ$^l4s3GO*hL>2*0&bHrC6bfG@qF^_b=V+;=Z+T+j zdJ*y9<1p4b7u#SVXC7c$XtRc++brTF%m3-~n9#1(NAOe&6FBet!v0#LvbT|j!CFn- zgsYn6Gjj3kcWTR}8zkKnvLnthlYFyV8|kc{DBZ4 z4BZ=Ces5Gq3?RdhdH{QmeP$grQt!bd7gc*#zb0`sR`a+w;5pD6pwKP&d@kDI{Ous|>?QSXXJ-L6I1JbKHxQzV4>V-l8Fh0sl zHLGTEPG{|19-EFXfl&DbmGY8g_3Bf`KSnhcN`!mc73liX(2_RPN6+;FDnkwo_)yy| zC~OK?tricgMpwA{(Q|eX{qx=}AjfH@r;T2$10niD!n$_CV*wuyGAc;yfXw zwe)4#MN0zI;y_lzro=8PQbv>K3vDbKA_91jcI3530dDIJV+a#K?j?GNz<4N0v~_)I z7EgSoHxNt%{^;y>>+-J8uCAsSYJP{c=^yS>>K6+o$S$(065Y7Lx5Iz`h6Mg{myN_;U zTDQW-T)(6P?nyQM$)r-hg`9}xAHr9LbC`GeOst&;5^q0uxkPL&`+NwmFl-?FK0)32 z4>w+$-NlYISfqLt4@Io^{TtXk_cWi7)rW2F{xoF_TVOu!TWXm2xy!ac$Xjt%o{~%& z6i^mDK!HXY{ko()TO$HQHxPP224De&8&v0bHY@SNzM@0p#qEtpe+LijT6UHfxqKHO&H1R}`87zdYlDdQ$Tiu>bn7*@-*s)6r+LzPW>~^iWgSFXJ>7-O> zvsGuc6QIO|QmtjS6*kB}!T?`iK)?j!#XxB52Gtzy`S!ds zV;S(@5l9co$VpT_|~@I!fpi% zz4MI042G0c%@t>uCL*rOrE29~&MD0dQ_%D?yop+1=8+{nZ3^W$U)3y=eDZY9*m-Mv z{o}(^AUT!S;)G|JAr=cS@Xp9{ty4z>Qz{Ms_{Na{q!ReAuL0l}6uhYuRX!o(vlf7N zxRR+e8>&150MrSWnaSdz7!`JQ(IRDfOAH5Rgwc|p`Fy`E?#$TFW{ICIKM~`a#hTvy zgz4s|Q$S2ROP#v8xobeOQXT1;dU6KS7emV3Iq`EMHBewz`Lm%w&HVCa`xA^aHv-s{ zTqYwclFTx`YuOsGzeO=&1L7e@A!0y;I=r@@h(r$cpX&I@FckU|ZwFy~WjZ=$-JZC7 z{;JXRulal9>j3o6*YDousHn~PZ<2U|O~`Vk`y_WH6Fr51PAEU&?Wo{zf%iLvbD9M_wOL|K|xw9d2?AWX7kN8F*2 z0|14y1R7c6+BE;SWroEmsJ282X(Z^ORSku^g)y^LSO7H^;oHJ1(@eqZM-rV-bzyn*2G zgN%wbZR`ShhrT{IB}Wz5rqDs{`Fs>ERv^f4IIYt5e9Z!lI#pYGGwDQ`G@9h76cp@p z#yvSc4Rnprb&omFZGUuba9)%o@K=eS&dqc)%8mq={&W~BiiN4;O!0(r5t&<$tFuw2 z)05!z*~l=V;ER=_&j9qi5`eh*d>XAdeBogLavB+|m*~L)5IE<(sr#y0O(?PlkDkC0 z#89jQQcNnO;7+58=C|xM=9DBYth*xq%xizhIv2!T48(>Ou9gO22faIts=8OaX=W(| z&df4utcmvt%Bch-Wr4Hl^`2kj3>M0pyzS(9b;dPKy4mS{YnY!}R+aqjWF#Q!PON(( z4IU+LC5C=;l-pW-w$_lK!QyIa1CQ*U+Iz4`qXB@-sGS(EIQ88ev`e|5)JK;RVZx2R z1Gjv^-`)$DT*8-zu&}pkh6zn<*B6){T{?jDA_mw) z&1`lB>*v+IC!c%oN^7QY=uX*ie#ix1J+0H>ybU15H5QBKkwB_j8YW;lOz5BtwN;>D zWC>&S8V3WAlLZ~{_c>}!^8q^=me^TXMr_#MPq}FoB2SS2iVA`A#F&>1vM!Y#y01Uo zPsDg=-Y7JO`G|$3F--WFPt<0r9J7+L(|dJ|WI?#gGa8N`Z#1vPnLtsTQ+a+A zYvdh!rJcON<{cmLl1{sLq=1Yt)eQ)n8|roiFyu z-;-c0$4&{aWE7&%Dj+$2DB|gCh%jr(A_}@@OcJcG1)bqcSY};qCauLBK6-R*!v1AW z$=F^-DcL1ZsYxq@3QW1MBa!qA#;3RtS5f$>g<*{EQ^N{qwx5$a9~Uv3r_d{9)O9{{ zu(XA1dD~ikvytvt%gV@BI+w1w&^ztzfn6+X9|LPoNe1~9L;Bn^H$PS0 zgcvhRVzTsuv3w2JnQ;_-(~a3NNc;P-9YW5*kmEBMM9Y(=j@EeA8JfIrBPJzp&aIiW zjCZ=FK2kSrv}jMV6#8SN{yDJYuJM#-=NPYfy^-**g0YP@;PZKo|H^6sny1*_)!66l zWsyKw;f-PNvma^B_Sor}b93R3WS{O#{^V-)xX*)3((n_G)hPyO+y@~3;aZd}36?2m zCi7lkBS(+()X|&lk#u(g;iA{I+S>bBPu#(nUZMvsEIAy%sY4F1#0UPDxnO0N7kJY$ zMRYm;n5i9uuixHLO#Hgb!1Ky0B6{{AA5Wx77y+pkxhrkPXKZ`Fk{2rXzo$%tiQhYo zGwtaGoa;8ykFC2}3+z3t64P^+X+7W#Jio#~w^B>UYP?iU#-KUQi>4)Tzp1Nz*5 z=p32QThLVw;&XqwdM9&Nvifz$?w0c-(4Te)@*wUt`0(Ahfm@pK{KK@1NtW&8oMpx; zFWVr0g}4Fb61#~>L9{fLMnUXIYhY?``GaA>&2}~8kinRIaug*47qOs#cTe8yItc~@ zHYrSe0Hap|)EP0G$4gQWlLQ$H7$w&q#qa_GcBuMhDLf0gv;Hoi--INZAR{S}s|=Kf z>kZX7R!K5jDj;)XJ#s4B!^YWh_bB~=Deo8XYQM%W|rq^Hx@hvzuslGpP!%MF0jzpnQf%KlolA=0~DSej6_2A z0k_0k7$uHDEv1XG1p%ij3vS606dmPT!r7hyCHo%&mejFCu{ZjIQ@JDm5EbsZR>=wG za&7HnwY|CTBl9bYOLC#rZE8Efs$=>R_6Xa)+z|S* zfa(-TfST0Xyxmr@Ta!bkRfcJ)7$beUjwn;bP-yELNb!6RxZkf_Oss^ZO<$#3l24BFT&R@ zDsw+=6|@IB(}Bum1IQ1h-hF9f!eOWoy;6l#i$@JW0~})snABo|7RhC{TB*>P+F`7ijtf%z8-K~^>&;eUthYz?O?}f zk5B)WgAclzDFPnN4$pzFfy%O|S%WgpJg#!NDu2>>V-uR`g`l=dGoS8*3v=`Xxuu{E zj#s!EA7|q{x!Lc&R&a$%)h2e(o z?T(Ee6+vTZ_^9S9=?LsZ1^x~Pn27BDv)Z$9m%guv!dxu!ZS2p=UAaV(j^)LHcohVQ zcfAZA9vxj0vSJbvonIK->GfY5un3nED!u8D;WM7{M1~aBun=@cChB~i&wtQHY&+Uq zfp__<%g*)E*Yni%u5) zr--*WTVYVb^0CM-s1b}(*A!v&WGD8d5S$vY)}=)yp6L6Ybj!2wp?h`zw5sCRuOnxI zW6n!z-a=Y5CWoHPqHKkrX|y9Z2R(h_D3)ym;>-^7uJ78u>TOZ3pRj znjqMFHpzqoD0JmkX;7^O>Y88CV8Y8}(V{y^TFWA|*rr4%#<^rxu)V8nE>zYlT2DY> ziH7O87FyKjoG#v0KN7sdq%8jE_v6IKQBGP`q18-7Mgyyv=--Ha z |_Fjz&>G@en?%h17y* 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 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/1-piano/72.mp3 b/src/extensions/scratch3_music/assets/instruments/1-piano/72.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..27451fc6a1914cb91939644c065d0c098a54e4af GIT binary patch literal 46418 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 KNDTS^=lg$vggT!9 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/1-piano/84.mp3 b/src/extensions/scratch3_music/assets/instruments/1-piano/84.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f32a1da73c948532fa102b80ff65ffb4dee668be GIT binary patch literal 30729 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 OBL3U|`u+dc*Z%`t%94cu literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/1-piano/96.mp3 b/src/extensions/scratch3_music/assets/instruments/1-piano/96.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..4efc40bf0c536e7dee1ffd4ba085ead6a3c2664d GIT binary patch literal 25596 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 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/11-saxophone/84.mp3 b/src/extensions/scratch3_music/assets/instruments/11-saxophone/84.mp3 new file mode 100755 index 0000000000000000000000000000000000000000..7a961d0dfe2c257ca55ee7f1ca63a869a10e5c14 GIT binary patch literal 24649 zcmdqIc{tSn7e4$R`@Th%$k>;%BwL8F?>pJDlQkq##EhjZl`YvS`@Up}gfyfgDm&St zjqD{$X3V^v_vrKeJ-`2+{~y<->*|tb?m6e&=iK)>Z-7ttg8o0g?EQj#;D5ple}_Ym zn;}F&MMKBPdW?gMhZiX%A|`Q4R!&~=oT`SVj-G*$xuvzOouiAJhqsU4<&e;@YuBUE zvGKQ4?xtqke~|a6ps1v*y0*TdspWac3v5qs-@yBi!=qEvv-68zmwv8oZ2jK*i#sI3 zucc<9X{vix`rNrtiU!gOv3= z9ED#+5Q6yur`+>>4E$G(UigP(HpV58fD8>dcg3ZHu28=TZ(nI1Aux*DETeD6FH9t_g{}d^kbp9i%sT6*Yn1y zCyW$_k}PsPgYEVYlIyJ<_@))REY}uId%=-St1Zb<<96qxOtRylElS z?FK*A&|=chmU<+0YpYg z>xMPH`+5h(etWqaqCqxWmBh^6ni;>=aF}`L+=Fco$<*+sK~dYU0;A}^&n&5*-|=Oh zeR6@8OvSIIbM?nntbrSZm=n1xy9hyw%~LDmL2W}fpWz*O1Z98_+x*6Vq5%Uac{ZJ3z*2g-k;1c|salyaW9=n6E%*fq4?UC_V{EySr?SY1Z<0ElO zyTU&J;w7(q577v>*YZEU6PDriU7|S+-LSbC%$u)F_m26-W=_o;a@rQ$mEm_OsRxe{ zzlLVNVQe)H3W?R=YbDqQwWLy&HGm-3&Q0`~!c(ergwDfQ_kDNir;81r_sxa}ckl2n@RQIvmoIvOX(GqECWG`gog^slvNyAIhWbZfZ>5z1VQ_O^}NVt0K^x+ zd>6*kWz73ZjU3!Jzq&cmUKdh)lTt_Ge*O+ z6GOn4p^rBywz5fh=-D8Mun<7(sNq07uwYPGZBKaUoyz@&vG!sd@psnf_|mbEFWzrE zd_uqGr${&Z-j3ZYr64}|T$A`EU3Z_5RNR4`DS1V}@|)Wc43vOG0sKLI$Es74Y3v_X zexL9!w!+aETRbb>6Ju4s!b8GCosemyW8RJPU!;e4lwdq`68g&J>ucQtkPpm^6cjJ_ zH(Ll71D&&I?344F2z~o?tp&`VLUXXrUuha5@5NiASNYziSheCF#e-47lVA`0!2;RC zoMngxu0Q_HRrDAxN+Z6Z3OnMJMo}bo&a5uzR%Gsb?^~Q7KBmjWmU!E!4mm9l?mX$a z^FTERv~P9xJ@@Nu7|UsEFEV+Wy72Rd{su_)WLm(313R#J3gFQP7T5U;aE060a)wiP z4vW`MxloNgs^dP2Pg>?_osWnpt3_ti!4?G>Elv0_74l7ENC4w|5ytb;wNv5R6PxT6 zx_Xhq+vSgjI(xA=-%_fkhlLL&a(69W(=U{(BMm{Yyczh+fhJCXt>D8u_?K0r@Id4k z2>OMs-Hk!99497Wc>Vq+N_4I@B7l-+R48p3YjuqZBthOuo<9t+iM%(r#qcdaYknYf6_G};)JnJg}L zb-yphey5&#dHSY#Y)0#6(FmYn9K3L&# z3`5iw9EL8bZJvvsqtfBOgYK9=zziD=WZu16`=+OJBKt*uoOz{Vau%_j$@n92t9D%w z;_`$vfYA z_5sVb!~z%;bQ(bo|9M!VzA%bBb8~vm&;Lxb&?BUyPL#!MV&?0Y66UwwMcGS*gmVP- zm*-%wFJId=E|IM{zOeB0QBP04$_cpET>yW3nZ}k3)58D+bGgOpS2ppea5}v9ad5Ud6%vp@T8XO-o zwh&rlm+~ca$Hgtr&=rdd1xtj%f*Tc5ubiuh_bI{I^e&Own6Cvl_gYd~8_zu~6&^fN#({|ygkRJW;e7nxh{?WwL(u^yP9 zxNg+NX{Md(X4n~Yts~)~g_`-{d~?dQU`Eop!g%P!=anl0|4)46D)Wi6ubyLa$C$F7 zjjKlJ4k4GihkNqJy;m=HXm@dfC}#N#kTry!DY0<+p+Q{u3_)DSp?EkxH=Du^u|E$J ze~EA4+Z5@KLpY|Bszq6XU&BSV+r7@tK9g#R8+<6B`VM5{ALF0z2MQOt=D+Kk;C?(z zW{f>sbJ9XT%oNMgcKGKD?_1D6J#%fyr#uiBkJB(kd7>XuKHzsJ7kGNg<-Ih$`@N42 zr|*#P&@;0c!FcBKEm)ufc;2C=IqXp+-}df43rUdbXHd%aGwW{_uTcS;{6s(2NMB@% zXo5Vq!c6?*XGtCF^(|R>=k@U~o`O{|YK<|lM=---AjB&aqz*xJy;=(qo48(YG*6(X z>==UX7NdyCnOyFq*H3;p3 zI{hhm2hgeX;jWW5_tB-u^8&k_0AxTA1090^27=TRgeD7zDX^i}%A(6CUx^l!<+jju z@Q^P8i8T772cxGBPv?3Jf*)ztVmbHW8nw}pw_!9=i?hRy_F z6`uZSAeQGGw-dYc)v+Tg4d5ZNX(>{U{mcW|Za00s*3O8C+YWssJPgci$7Ri+IT%kj zPa2$WG;{Nch)o}i3VG!Vh=G^KA%>BP#W$kjSL@JZa5Rs3wqapDA^aqt9YVN0eCWkIH2ns ziiv_hM=D;p4I$WlCJV>#J$7kCzY?!I>lSK0eC`Rccg&!isTAwauMWrizfeqgG$D_z zNvH2^{+{vWL?vlP78bO`DmCg(-$O1bK< zlvtvO{A^%66QSdjo^;&%xj2nhZ^`wKNAZE0?Pbln=Q1r=p@Rj8lA5-$SDxaxVfTN0 z?elK^PqwNX$~{A8crG(+uT{FVSd~#k)XJp7tZbCquFEw-S;x0+FN+!MBXwU6&62W% z8cvVv5Oj*h?4TEGw@Q}rw#(5{n_00K@=W5U$}w=9Sk<(R+6}D?BP-E%Cclkokgs@a zd;042A6|_DF3;qqw^TMO;ul<^jA+6yFMJl(*yw1qXeI7PrlAj5h=pVBH+MqzWCwZ> z+Wx}~SyZOi$y{ARIcG_D=rL?YN*|z`a6iTTax@oGag^yCb9io9M`f{j+@UCJ5O^Jm~2x4fG*c1vnH$uGP)r2Fu+?0WXKD z+j%Him~Ej7+?-!#NDJAsBr+5JotO19LQhQG^>59lb+jv_Cl>NMO-t6&dQOag(0*dZ zkyS3YRZ?2U`WVbL8pz;l`GWrXH~4HG?oYzx$pd*})F+iTaSuXuVhx1GQLRBcH*ESU z%N9v^Xb^1rvO{0;{k!4%IqIkKz2_5xs4?;wzZY=6m3yA7eKnE2&V0{&OQC*J7z(0P z=~^`?i*F8t?kP5G;@+OojFc=LGsrMCB`(wO6TFf;UP=Kl48JlPqzzZo&ENY3V4W#X zi#o}W9>JD4P~Xc?_u}^8MLK)CAd&O!ch{VLoEPMdHS%`8&oepb|D~Rb16#k#)EsWK zahYaW;!*?AN*ar^&7xbuYXF!%$+a*nA=Gu>ocE%Q4yS&xX20L?SAZIdyGeMcFqulS zAFE#YEix6+9raUTI6fQIC@x5X3$BM7DPNZu$U7_vp6Ansu9Z=BG3bdWE{R#0#o=NT zD5-2YR7^iVL7_Ynb*hM?nIP$^7w(f+Oj#2jHSGK{kgvrt1z z^&3O)A;~6QmXTwl1sN`{K0n>8SkQmZL3iNT>0RCN#)6AmG-9`&^|XNE?yfcOyCQtd zB`xs#?$QnOcj&`K1LiRt+LSaXKxa5-i`s=>bLL`rFWtCu)PLw8Hg{PwP2NlkCO10L zd`)Z9dtNAr7Ntz$Yj$4D;K?&5+Khc9hT8ch<4m6MxsJ}rkCq4iO^z2b9WK`75O;W6 z=-k@)=@PiOA_6{z-^Tv{B47^#QBh)$i}09WHL?@)P=W(d;jR@049X-0`)?sWD7C!7 zIF4Uv69L&9UyiXL$PG)?UY3t3)YD+;9yI4HSPbRkCz>+_S5;?c!rFlK2lJ8TX6$Z^ z(+-?76cBWg?$t4D@D}btO}ZtI19#>Ar5@VVqxe8d7ACM~-}X z2gXwzH*^l#77te=49GSB*9!}J>VBGAVnM1;%APbmrh6M6?9y)YY_?(dzC(RXg~z*K zP!*%SAF~BeuuW!wpo9O%opKHH6|(c88s4&=^z6*sc1RDm%m*(s+E?_iRCb1{E7Qu0 z>jvuu@{~K8d#6J;x${me)Hw@)?|QdfZIi7Q_WpQi?*q~#^$kO|XvO+;lK>oNfA-k0 z3WcKHiC-mdwp{X1U}!BT;eq2LuRaoBrB(#@)1!Pd>D8#XTnj0a`VS3nN#NCFiSW98 ztIcbd%GYm&Fjv*A+3B8CpyX(=na`Bt8=Fc5d+f>lL@};5;yCz$B{ss;+DO3*i@6^* zX*mSxSr*zjc0XCl0j0_l(x==GnS`brPzg%ShS4G-=S?=I3(7p)tva1bt!ti@Bxy2) z%h-Jucxzl#HdbDrEq85rcU%fR@iug*KjtO`33EXdi{=bIs|0K_CC7GtwlzW~!(t?* zf$9{U>~RtvS}0Q)o)2E+EyD33^;6o@Z*oUbqiEDA|$u z>L0e!K~<7T`PNQ%ZR;9O1t91+P`fOIGs!m)f3SY}T(}9q8U!&iK$hL`j8I;?Mqp;z z#E2JZ-xH>A{#&0D^xn{GYdml02K49H_2m(#hvPxZa^*<|J(Qn5FX$?a+>yPiVeHbl zqhu;G&CV}B;S^Is4kF`B3Qzn^B4oD5`t{G(e6J+ z9ljMjqI8zzYH7~SMDIVo9&dL1!&Oz~xM98ZYb?md_xq0c zA%D|ws|t^W8v&VSH)KXVL|wQ~FvqQC&%~NAgGClio0Xnc+uV>b5*`}NwxK$VC+>fI zK*!#zD!groqQpo-FrMMTujGup*WX0`Innea^wzyR8a2i!kjUPzwHlTDWZgZ$2peONCe}vNFPT-jHAsVQ49a07?ed8(vmwJp(wP~%US-&X8j@d4#vdZ;=>%_eBe7F!e%ViD*gxiy)OvdU9i4B7> zHJ$dcCM=~vvXt4oEn!8|_ZPKgj`9s7XQe*8q5}6Hw|{!>5ng|z!pseck${H`s&fN< z4NnbT{1BDT9Xy!UPKA`NUY`&AV53D*Ll62!`O(#udDQ+`58p+7e1rWOSOW)73IPr_ zieEttZg5jAm%w3-6MB)XMXW-m>39dw7IV#<ahPw9B3s@+Yow?63q6N0Oy1psQ|yKl)%ByN^C_gV-%N$6IgKGtsFOGmLle zd3PhNhw3TgivPujO-}YhtW(}E1`M4EoNvh#c+CWuuh0IQ->&A#7#SCpM1L}-!Qv?0hlZl{i4A}>Ys(_^;Dp6xJN*E9pU27F5`{8iOBu&4 z%OWVoQagUCY*IZ@+8zvjadw_)ckMWD4`0z?PPMZp75(IzOWg2r;q$Hj6zwy46-<%pwijXvfpe|(9{y!f+}2m4V*4AJv9W2ANy0bSoX)ee24?{$6`e?> zFKu$`A6D}=3!~ophENzZJH7B9FYn<5RqFf1O=-$PKv#|hV4HvBK;t6@7XKLNBK@7S zJH*17>`$mb7Ekv^S;Y@sIC(=WX5XJ)NAZE=JmC6y2-i>1zxZ4{F#I6D<0b|9J%{5H zhAN>ZR)p;Z+jK>Vue0ft$84VLmonzVh?y#&GiVf431RW+UR?zLSBD$GL;O<%00)UM zD0s{w{|BntTRm-bgy}7r5h7|t=Fvo#r=t}?V>HLFEbnqAEe*jPn7;*HUHRl>B)x# zCI2Vp2_Btnqws(U~vBC+f#m$4de;qR`%~ zsaS%XFgPr-#lk6^2O>ym%!`B&i&n?nmWPO2H3*&aRBtNzLMQnyLOz1d|KT|;`w{B2 z3SmKV1DFqJ8u8lkm#b0o|NMhjA_s5(GzC?xAd1{3?CFnC%hPGfV96S;#*~D}hu3gG zdO@Snzfwo-2j1SC7gqer^}GdpQ`@Pgm4fEF4|Xlb7=0Kj1R|6*gNbJ1XdEC$0WbhT zl($F*LcQ^UYyU*3JVu;+s^?A~`h&^sNHcPY1QH%vh)sv27gD;Ji&nNEK}}j6yk`7A zeuD=LifA$VRFCGH(iWd_svL_{FRsM*OkX}e(PB~b>z=>s8b4*g)LM<^_z>}jAR3H` zV9y=kJC0@^j0HrGfylnTCumge%?6-E5%^!Xyw4XMk8Ps5Bnw^o3wi3FJQL&^+9m2V zG0DbsHLA`n&CW7}>!fo4!SiPoq3Z;b=ZUu9=WoEaS}j-sxDWRM`0-yJQ;#aTcQ zh&7=^LQUAi3j%_SNBl;O*>)oJQ|4qy+`~;g^@tPc%=M3MzZA%Ijz5FJME-IY$*jZ zk;5XZRu6Y&Cl$Lu4azig>hLnymZ)4O04x}caUi>G=V6)aJ*sa>eDyOIg?$|6(bSsTgjVK?U1L;qM4R$ePv>*ouR;5k+Gj9*KCe$aPT>T8FXW6b?M^BUUAn9r8Y-~fpG{2-v zwZ{_oYaz|K{Yx_yotLRu+LVJlDBx!cUtQUy@i36GTLRgG&g;2UfGK0kY8Vf5T zQ=5cUeclP*q-D2kg`ofV;DYSO?x>7K=6`yQR;^bN_ODS&g!z+*S7RQtk1?Bjm-q?h(o1$MjCYyfT(5|>RDq+=%ZTc~JDY<0SL=;u0 zP_g=XkgcrCWtZu=(ZVOR5@;^V*cyrs!U6N2B>M=0{<7bi-j4xKLgiymQ%<0?8CPE) z&DT^An=q^wc#ix7lHR7V=v78+N?>3;cL?h_<5Fp>&_{*EBTl|U{_{_BwjMiB`P$g2 zh(&JHsZ(9pvdXD?X*o*p)qgxoNEt@svd=b<)F}}ILBmD=Q+1U)cd&bs#xvdQysPb| z36ut?Uq6Ih-UR;on8ESt=kIDSm%a17ba#|7di1yb{1=8f{Rmj(-G}J+dcEjv`35`) zK8m`5EJWK~z%?|1M|;otzY1jeo{f?|or?9!>ppGRQIq~J-!k>pM^;p=EdJ+*yg1Nb zzt@7~gCxXZJ`hAu7dqxm%^xbviGIac+}(_rzkY#y=Ow}3mK{t{wVkgk*tFn5ul^R6 z!tSqu?cfO;WL~1Rl6}M*j?~gb& z{N4Uo{q@+QXkg4OgR$ykiz#bPTg~5Cf(+br;TMd&#`GD_e6uRl8Di)ug=8F9T;EsMo zZPaiD*&CrZ2@5W>lk~;v4pdIF%J0O-HASEYWtY!VOGfw}gzjAk&u;X&ZO`8JTy$cj z+3hX+Q2APoT3;HW$v3WbYW%geIj*T_7JNVxd=_@l@aP_?-L{3Rfr1X#FG}-zTw-8| z(a`$~-P%Cl|HX&RP019Rh4%|ZJj|qgJDfi+t+RtV6TjR7*H4=`_HeGNTbA`cm2IGP z<4R4S+sBsiW1pM{%flze`Ns)pAKm~*{#p^eKj$t0zvrbGUPCHLZN zR~-&(cSwgBGk7YdI{)XHM5(n@QVp{}T6(Ng+588WA8H$s`t!BiCeZSBWNqaq5nWR! zv;~W_>p^d^PB<~M7$(tE<^+QnT){Vmg<8BNm_8Qk-a_yJn z#~0~1f(aF44S?jI-z8pzSwI{jdGZI}w(!Iv$B2^Jx7?o~uFLk8xfsK}O}!si?nFr0 zS4Kd{Kdi?-pBh$ha=1`?zazHnRh;9so@pU;y}D4I4c6e+U})0X(C_<5LKN zIaXMrp>{9kO^t;Gk1eg4x81m)px7Ee7Z}9G(ft7f)SNAAW~fH;TNMVE>vO)~BjxF6D=g=U=keDEE7$ZA!RX-TJ}%g@!K5_;TUd3*mXzsPw>Ko6Fuh z?E4*&vai`AV~X#@q`e~!0n#9R8tQ|w;q4bXSMfKyLs&e-(?5JgH?*HUnWElwDSt54 zzFyRM|1{~(+1=H5}B;5MnY3BEBUqGpvOuiI1PbU4VM(KxhFz+`Ct~* zje+$;4BC9;&KLSOwH}w994`?7c(=%2#RF3$7#Ba1=y_)qrL2joZUy@!B zmAgjc64>28u9YZl=_+OY=`iTyKyUnh*ZRn zfl&Q(djh&$Qod1P&K#Vv^fb964)dEi36C;fj{3JT<}@S$XFr96pHN`&gM9I? z1HFB79y<%EAMd+Gyud3&Dz{Rf%t6u_v0!3I9{9s{3{Q00!9bJ=|C~|B``d*@ zEw%x`NwL!Wi#L7Z-L5h@1HW@_r`_HKwFkAG%0a%?6PwKMlO0mu-m4b;u3dY$ayQyF zehG~4P^T{t$drJFMLZaTZ(IN|9qs_#lz*Eic8MvSjKCSa84l$k+!?NWZ)!IhvZ)>u&3`R`&k)3f;DSxkoje@&nEY#%ILTN!#bKI& ztdtz-?Hjvy`|?kDvA%ZhUOT4vxy&fO(JT%jvHQ7IYD9)=53RfF`1%I@=b3|>f$ba8 zxHq%m=uoL$v2A6z8X-DZF%na-i|!|@D9D4lB2Gw1>d1H|@*eFMXfc^3XFeF_lkSmd z*I_)#26+8-k-8#&jCdG~=Mj|#gAe7vsW0>s`9Ag^+hc+UPXEZ}GH&h@tEb>v^DXt! zcYhRRbEm^`HRP4x$()Tet;xs^#k$Ccdn(YOj_3Do2to3C4yPt?Up<_AI8b>P)2aO> zeD=YG^9rn&tcANwhDSbmkl7A2XN;rLB@_yFCs4;Ht6j?NqnW4(hx+##CT1;Sw6Ife z%cN(Cof-r^0=Fz|^k~?v5ESgL-y+v06ErR2+_B63S3hjZXDsld@ws$GbabTn*!L>k zO=X89Au(AP&+NpZ(6#DCTKh8JNusB$rg**9YjLWt4l4mJ9j^~@W7oo_m&k`e%Jt9Q z!1kDjQ!VZ-T4?grE*3U@8Qfq`WCEw5nW}>jI9bJ6zwvFTwr0|&SmsPO3-O~jU&IJqLlMbjd|;4r)A`#B4CBs{Qx?V@fOC2>a_#=}FxlYU@GijTlQ{j;a` z$rWm2g6>IF)ZMrkla1k&?l@(w;(I!aW3QTit>W9S{k648k&5+>mmRm#(cqDI@iXic zSVC7pkU%$l(c`*CFjtW4HYn20cpLY&vTg5dejd8;XYIU>rH4mCsr+Q9Zn`Aud^du_ zxl^L~x=hCg+9@p#5LT#VvrQ=%>LuN;qKRJ@ws0rV9K)A@K^{7!ohAfu>qDUY;3SpfuIXC*pUO~ znc{OGznejRK&zrw`v#bmskHaa&n{_uDkzdz4A{2a?oc7UancqplrBYO0Zn0}K?8#?o9({3OTx zE6(v{6lM3=<_E5C*$Q|~5J!9NUB*?>4S%ZBb$ zq;ZQzUHHfpg{WdYw3?4Ja`{8|t2gFS@@Ndr)H4i`oPNV`|6-97n3S_ZZ$0J&xW3T> z@W=eMMjL7IM}dK7>|&aO;NcLxSaFekw=`*I;Z5hQ8;_l(D_saLNt30KwYm?QUkez6aGH^tDME&dI{LtY zAB+YAMQ{LfNn;~9FHG2LR>ZV^D*lpW%0&02d?qrLD)%#9#W%ilnJzpS``jBq5zs3) ze{qO!jW_DCoKXV1K}1_kzJhX9;zFwjhl0j_)j2FtSHKq3V-H^+ZM4W2qrWV}zR21` z{~i+-&&!R6BM~xfw|LJU#fNmCI2>^4Vfz2}r}6~NvLDqbK}f6-jt^fP=S&dS;V*I4 z*}K*i+*Pzu6#e`Lk@qGuGFL;nxI`$yT+B%Yr(Fj3)(Bk9arh`%pZp~}2Or%f{kIP` zwj04+n~^;RiO(gG(<_M0=vva1O8+^h9DO2uscSBH*>Nt7ZoAX~AEA=w{JVXg~%*H(N|qw1w>cpjZc%jWEYOH>VR3sZ0S4u2ew1%hoR{rdJ+c~|s{~2k>s}OR5%@*7wvA7H>y!V@qnWbmmRBY2 zllWQ^er>w20|ilkdWbtg3`vgA8wk*p)2jb)okM(j!m*S-kgiCD@!4csHCuYvRx5Lp zi4FGfy$$-%+8X?xaJG$(m?d7BBB`IF-ZO;*x&Xp#pb)Ouu;nMaaVoF4aXmk{U;I#; z7p}-pyKrsn-bL zaYorc8{Vi^=Q-p@*F`f1MAB2TFv@cVuPY=K+D))!<}=~WE9BCe6SpNkg7J`j;PBK5 zRE5yoWbREn7@FUe6xqM!Q2Dxs-RJS^A`%`dX4Wm^j~LY>y}d>96VFSjAcFi*s*o?t zO%%)~eNhC$a{MMc6Ip?FcwK3Ix?gStVyVII_hU4%S$G44%C>qC#@v#d(Fv7H_WfMY--0I~ej-Q@pzRDPjvQ{ng1& zkNxrB)aR}Ux(GKC9)>hFS`4Q(GxzM5rH10g@!~wPcK&ZoR*g^r31h z*FIPJyOQzFs}&CQ`ed%ea_w)}{S%pBIak~VyjcK90)HN%8$uwvA!_(He+g;$Y!EPF zd!d-VtaXgM``i@_;-0kSsmi|2R|W3nabyAyTDDUL)BQgLsAFhDq{+h+oyI4*gJ*xg zh!-Ok*93!c(@wB_0*vj#p2YGBoWzWbXS(^VCRX*ouY#fso*GETFp6%V)O>YGMxt$z zgoidZ^Rl}6yA#R(XJ2oC*N6`?L<#@vKk*qC{XTCJE;fE8Z7UW_WECEekc4E|L0$K zYoNF+qQ2tj3hg%Ml0B@b@s0hGgY1)-vZecfc`Hs1OfZPq2Y zHm_PZW^{SFkM0NLT|&HP%+o8KgRUOXCI?mCX)+U2sYAg18~Wf5`hXeWx=CARnh@$? zOMdgO6G;(aWbzb3IldgXmfD>nKfWR1p}{n}X;?)W#v}i?U${xoUoSCpW5kHVGYgfW z&1}*9d!4MS6s$#eh6mkikd)shDmsQW9db+6v+t~i_DF#j0r}O=xTgET1n)T8PT)8J z1}lNRoU3^qCh<}<%eii-ep#YeaQCQt*21Ff20us)%A{ux~pk9;M z##TCcQ5I<>zl~oJ*xc`Y;8I<=n{!T-=(*E2?l6}V{A}MNdQVQEts(6={aiowiPi_^ zqsO(WeonSN>qtPZf%n=x8nxdF7WSet}zF4D)C$$5@cjuBaar=iC8S43`z(FtEglIyJ1Q57rYiqz$b^#&A3qeu|GsvS_aT2oS z`}#J;xF%;jo8aVk{OfCK_*^x+Cf$P82D+1KZt7V5Zdg(dI@$-r_t*C|Ox`{8U;MA1 z(%zld{k|lDWWtEsz<9W*)G5}-oC_Xor%Dy9%Z#!TIM4Ye`))-g`)dwL$bw(x!as zMOg$UnTjY8lt_Qwem>dZm&WILlhh0zB$;acWMve6)3?bSy`<;22TW4&qRW(-^SH=C z+G5bu7wt|!XSMmi3SsdKl@&PknbelqRVhyCDvBhBK$A6PHYNy(^6y|Y2_ z|GP&z-E7LVlN?=jL-%otpZS?t`!jvjpEWcPx|}IshCKDnWj@gP{uJu znQ*xtDU+atB1UokR$F<;D*2p=m~|UXYkV!0XD~6=0t+;UfDjMBp%2@>LuVlV%I$^w z$il}L+{akR82U~lj&nUacZ7$TO$zQm7xOKcyZ_yj>q@DS^v}8YxqaJ-_(H}`>6$XQ$j^9t&#&4~n1dwc6fDwx$;n-Y@J|Sl`{+LivY6oJ{*s*pnP{ma`;oQC_O zDbNQ%F7X7R&|#~JXTy;B|52!J6LF%T^#Up(R z;QbuU=>y}0uLg@}Fh0-WH#IYpG@fNtdb~>FD-$;cBieaF^6&eOC#`lo{VGtl=JD(n zsek}B3hd#9U6yEad8myuF#5ak{7uGE%^)lQwfOOGzox+&`#GVN$flMk@CX@$!-DPP%P$)tSejCB!OCX0nV zN>K4`{!yKIwf@*cidKK8b>i2%IA3$qTE*iUCGCw)P+!n+pY#fUwE^X=S&)3-)3_O`UruEP%UDKBbYPpBwBWyW2T_&RCEPO0ok z_e13ZutRYtoET=S59&M`K(%em+$t%!$|U(iKv&GxQZ$k!zlGlTx8FHo7{SlV`}FLU zmf3eP>^V=IO8O+8Umwfi?;dG=lJc&*zBVY{;a2X_WE`=TL6gI1VO)_Ni|f0(7Cf)t zDkg&05+~nPT_xD28@w_QnnH2bVgoANbMFh-3u%(@&|*e)mCfBFlNVu0x=e~s@)=pg zMs@db$d?Aj)7|!wA*!`K_|Z}rb5FjZ>a5f?(^ExKZXct0(4PV_`OM}GtE@BIb}z^m z;%^`q@MTfk@a`4?Hy`ppppdX&@h$rr`HabITrAVa{+kH#*}l&OWjo{iJ6pw=lsvU4 zqwLiv=fQ}z>0FivIsSz_OZ!%>jVInapyz$GChZs2Qv|+XCp?PJwGmz6532`O4g|ea zx0nd{HlEdGBlP>M8y|HplbPG_Da=uPXwulu9r;zmOx^H)0p6eTNhy#&D*@Z{A zLfEVNmJ0iRP@UvayC4(Gn(*^BqeH3xeL6AT=4exOWBVPRnizQH~Tu<-oGV6Lm__sJ2( zf3BGQRYStj^MEw}`e`Tp{Eyqw{WZQO*)q*~EaPE9k=#c@KZWlwjdNN1Ah3*UW!XWP>?T zo9JAcR~2zpxn)e90?*%wyo_+{?bls$=PA37_j?@lAjpe#Sjx+~!D;HsAev||Z1Vw} zNFu3(-Ey#uHvf)QgAXowAo$>Nk{|!F6`O9!UCqHC@lF^zBl=a6=F%gKJ1O7jDA>f+ zhx%tzESOO|h44F|wK-4~2ol4n{L|Z&mmZ${vbzmGRa=aF|4t-&gjonmFW(ERjupvL4Haj9G^529uvGa zX1aiw7_be#jdOA72;NSnJc`$=ZmSxHSge^`H&0yJZbTGdH*uq#r%jq+z~x z{dzcTvYgIw<0`YuU-(q{&#%HX>uFfx4eyim9I5}%+7k@dD^3@&W5nU1iK@4WO7ADv zr#KWsp1xMzrsRYbP5)kgwQ&4T+B@nkPSpeCa65IjyD)DL9|>6XsgfSwe!uGiv)lg= zWn2FwoH=V&WYB#-RgWwq`^Wz8{Ag$KaqpSiCS3aWsPZNZzoy?FeTo!yo)1Spb2{xx z!*o9epBYby-DN8NwlQ^=91w1`7GhVxVRO_j4T4~R^H0tTCzmHX*}2HgDK|JC?N-;l z&VKzU-!Pee>XtY1;u;wsI#U=A9q+;a&DZZ`M)>9)jM{Y82~G#4mq5QO$J3q{7J@4~ z;xF&DHR8FLM~!Pj)CDV7{|MX<3oEoWXl?*m&M+fDM*$+ZmhN2ca$~2l?WAGQkGfoy zwF2=J(mb(jDw>Ctrp3kHmC#o%c&^N;-mt8`Q77XfVOHF1#BnXf%u`XDsm$)Z&RAXd zyHjh2p^?Do9(Ma_q3bFQhIw+Uc4s0#?-_+IS>3g6Rsnpq>d|~ajmZpD|L4E`xBt0F zFkCLLE8@Y3U4;2MqT%Utrr4%FDvvk{-xH21T4IhN3bX^Wj&x4%G?mJmv-U@YZt;P? z53&mhVYhX1R=X~|>Fd~Q2;;+?As7~#Xy&vE8X`^}- z<*&4jYGi?dkBr(y8>br?V)|F>0~@QuiwGC^dy~Omor&SYek4Py>)#f0R{|aJeZ6;P zwl7T)GyIgeW2T;_fzSkm%wXS{_WxE^I!cNU4JOk@>7zTWfBx4`#}8!5H}x?>kWUM# z|Kt@G70a|#ws}m6)va#m+q{?UmDHBc^VfWT;Y_UK4b#0(0U08-=;qfg4dF5OPe&Bs zuqPM5Pz)TPHzX*o4Lb=-xbhg|`EGIArw$2ME6G0-uRgth8_RP4CR!M zY{lvbQL8YK*d9 zaKB3hnN^mLBRHUzxO;!Vt`2rUv3B}>^RvLzu#sH7}0q|j&)W;d71B-e)M+?8sjfXEo+OonD-3 zc&ajj;mgJuM!qU7sPg}QQ=OEA-Ys9#TXw4Az669*$((O{T*0g#_(9`1g>nkP9KJ&s zDK#xGnsq@7v2IRjMxby-x9Or8Za0mqUY=L0s<+^d7lC!MNt_|YlK%f@^X1?bpe#T*=iw*NuTdKehO;* zG5dMq6F!z`>5WkK?)fR{+!v3>Lys8LQ56+Y? zkoj#_7IyN+yN8Ky4fd!@A(2=4DtKN8aq%2CFJMg%E4~c z*o`wEO2b9&E>DUuYZuP^T&P-spv6)1Mf04jh$~0Yx@fsncSK%f zm5=8o%_{3VEE#6y!v3?`PXm@s%XETCJR}wP>R|3BKT{18b7&R%;)OG~He0RvG1aDW z|0RyAC9O|`FOEXLk@pcC_KQ6SUiE>0*iE%dIbTnW;Ip~pEMnTcYH6Ba>j(D;kdB6! zC_L|P3UOIqCVQ>URxo7ds)w}7;@~-Y-b49kNMy-IW>l&9Wz{%^4$r^7T7L5b#c{f< zdgXkb>!AJp$41JXA~0}}LjO7aQBlYdp%6>VtVbest62^{DqGtMwW0a!> z*eDzyt8czPA<-Z9Q$;&1snQ!Wq2rFL`v>}&;Cy=-&g&tbBfxqDe#3sc_mGb;qHF@! zhj&bL3*!6zGLpqJq!r4?u$MAvI#J_--!1xPs;tND4JfS;yyGh883jUe@Of3Iz|IyrXG)K^V)=wc7ZeovwE|*KWPB?Rc~0)0%;!Ip)SsvqQR`^vX4J zm>{u2r)FZOW2u(8D03=tXI>-Yqb;p`TnuBvg^F-OVsjJ5OxK zXn^U0C$kD9D3W>ods=1zH{EQ3dg zg^Gm}Mw~}+Z10r>Xih#$VRiomZ<9^ny9*Y;+zSfX3VOIR4$C-x0c8W3>2L231g{IX zFvxw+y+#FpIXo|PNmt-#&aLRQ_J>kETX#E=sXZcMK@ZN~%T;b54vGm-Sg1I#bP3BT zpm5zlH<5Q@eM_NzLQCy6a@yiD?G>Zi+cI9NaO;8iXVV&=C^EWwUh}6h4Y_>co=Zh5 zF#lj`cI0_OxDpN%Z$V9qk3MI4wg9Qb6%+Ev<-Y0Q_Z(%RJrHp6S(K$$U{3#n*^*c{ z+&8?4%qVorlwqwVc?dP+K-)GgA)V5{ni1(wk85ZhFYB#>mF2Y~kJ@H9T5QiPxTit- zed$(keNcbz7In}ip$Y~pw}ZZ&c;G}_y7F_S2lA7MbzF0Xz=8xjrxEq{lBobHV^2X7 zKG3AY-%IFT|L1QbZqcjf8zSKi`%l!W-g5PRJW9;i!uer}H8^!E5|V4wwOBL{$j9BoTRdC`8XdS&D_V zG7+P{p#1H){Xh%JN&jY=1Gx~P0oq1K=B@GzmYn@^NKFu zRK1WscRqt||3Q_~Kg$a^$#6$Av(uVXi>J$UPurpnh-wPyz1YE zdJ5Hjog`y8KQ&p^@9^(gw&D60bVva!@O_g{puT>RJ{dSBx+&n0-WIRfVKE~etF&rd zp!nHTM`^}@N)q^|ZHiNfRm$N6xq*2us?%g42pWwxfwNS>7n;=M_LvdSBW)h7!>h6B zd*(|sopIN#d+sTn-(YoRx3}?3f8Q~QixEp?Iu&~$qiMv^Iu?Apt;sp6rw{g+!8j~V zwHVj%1!q|pAHL7gfY@IGm7^{g_1a%@%N~C5;q8r*g}C;$`~yUS%kS8nj+B8roKcNy8 zy@AN7WY3eh&ePSv8pn=kY@yP&oBUMN_2On{jWhixWd`Dp_E7!H43!9ecCmJo?X6a6 z;XiPHZ9ZI+=cIOl-^JEZcSDWJu~6dTmMROonUH=F0x+~+ocTxt_wn9vM=L8rjcs(0 zRp0N-^g)s`O7IY=dOP!Mqgy3rb^joD3i2a;;P*2oc(6b&$;`m`W45Gg$z5^V zH`FcQwK<+!57Z|f-RH2Za#C5?L~t*6eS((^ipCys=X36`p3%5*adb~9atb|>YIwk` zEY}d}HD*Ss>?66?dQ(%xJ;8#osW*dOyrWH!Ode^=8=(wLcmo4?dqa>1@F#}S!MS_t z8ivHH_>o6`gY4pS)#rwpc7;_zbbZ^-<0EgnUTZjw?Yu6N_F*8T26wew2c;BEIn{pg zc%f;(%d-ocDXca_Jja*@hW>Xp_lW$A#~EFd-prHLqiRWK7&^23x%CJNRNaJldyP+& zh4t_cFWVGlKK7A-`VbV4CE>te9`5G_$wLY^2|SN{f=~kL^J&x~Ghf+($*M=5X3wOV zAAakKykRa3C@Dk*EI!>Xp#P}%Y=6J)yW|eAsLpEHIr=RWqW#$C?r^W zUS0*-MXmXv;1GZi@=DcDdb0SSmXp8 ztYOy2^%)ALWu1>!2o3frMs4ZvRCmmp4c>KP@#nHwWt>ZZ!q1ivb)(%!O73JYHa&is zYTiFYeidAA2nj_=1ajCM%6kfHoe$EMgp-J=tBpiW6r!w_WTt6*$p2fdq3-3?MG3kaY za|ULGQi$$)t0rcs%U+-R_sc_AuTh~K@|z?|zx^B-FzY6=;Mqor7>9ags3>mbRx{-n zeS>v}kk(@2cSuoHJR*nJ5oz-up58PxK4V_UJFk{iv2dZXf7&+(*NdhqRMN8d8Y@h4 z?A5A>52fD_S;_d7Yc#NJ#cC;M7}8bZFda`tY%i1TozJ70f5$UP>K5O9k_CU!*K`K8 zHhlGc6O%Btwd49V|IOc_GIdcfuSR#xzulPVWsQ!zn;TUUA;PF{OsCuSD&D#AIz-)ARD~k~3)+15%b^vWLlEmLleA-R+Hyb*ewhZ!47VM&AYW?IO-UKF~$wz5l0msb7G?dvlG(3kIJy5gfWTMey7F!7!SJ3q)_T{m3xKMXpqfTb2Cu$=Oe z0F>~Ys9&^cuHq%{MF*FPAME=aHZy=txmo0U02iUSJ*cDcd&ZdQr;qOC;^G&1FFxKp zZ}mR^NlCuIsPd!=zQ%UbHv*^1Aqdp@qyxH&Yzz^u1U#Z~GLYkS_VXr8v1{%I#M-al z9HM1N1rLvlNZqP6$Lo;A!|qmYez@u{5k+*o_t^)+{Dbvh>#`BLtw8&-?N7+>>|E26 z8$V@FcAfRllu=_ExE*ONmdkO;Rv+Keq=#ozG!W=a74Zl#&f7|y_XefUW}vqKoy46o zqKh$^T(WkXh%B8fBjCP1-9wz1!?p{5Qm}Z%ld;n3?I)4|&vHbcJ z*ZfGAj6!DPK?}*PfG%oUt#Y1I@i>(2r`1wRq~pPlDQ@(Iupr`H5(piFkqNiadKkrO zAL)ZXh<=7?b8Gvj$^xF(+Rj>k@Q)ydZBRH>&p^C% z@Q_aoYKQe`uJ#{zKiFQH3-KI$CQMPH&o-vEwv5$DZ-3jae&4kBUy8eFIwlK{=wQtOjZRTF* zo5GYu>OtGWjHg>Qkp+d8tCwtOHtjG^ zg3oVWU>(AL>E8ay+(q0l_jB@;*Uiy?C_py<_()6%DK5_9qv3(ZRQsdfGpar;6nP(B znXILYqu9W0qAoEVlf$hC<|8R;z5kznD$or5fEJFqn@A@P!g@YecBE&? zCOmg|kM&Ji|H;?leo($W6}7>%{zE;=XNWa$Rrs{zGRR7DE+)E1Sb^emb@yp+Vo3jG z6WcluqN>q$bvR*-8vWDM;q^!7MQgT?qSNGlQZB#Dy1o65?|wq8EHk3-z+?g`*WSj4 zR^i59U(Lvs*?h8gRPxh^!IuV5@am-1;$zGH1Z2ekQi+4-FNUpGOV^UbeweuRVwZhT zw#>=v+)|a35Z&oDj_zml6v2|>U^z7GE!Jf01-saul zR!^-AlkWW(!SLmgqOk(5kKbhv-!bw6sjp@@53sCT!j#Fh!l|%`e3^``k{}XI+ zm-+081OXJ2HWTTFuH0eSgMW5>xfw>%!Ye0x5-m^y^z@<#RPGL!E>WT@KeryfWQhZR z_(YR6K2eydhlc&v zWp-@xIAdP?_J&!!+)CNE&es_E*ip;#lkHQ-Dtu_639aU!M;Rt&l0({Xtd0-+ICf0;& zJ&2huK68B6)?n`X@RM|`_t^&w#g6_v&+>u(lE#Ch>LeBTU0?#vXh+FUoBX|T?Cgo< z@XzaYM}+^5s(a;jq}tx+Et~8ix)slQ@E(*!Zeh)clf35*6glrtM3~L^+Kz6 z%s-bOt`8v>;)T*Y+fDH##EGOFg+TbEyF=q{vgXwAh~XOyiDEoGwNk7(3FZng_@y&6 zP@F;l*~&)WiJ}O?gW)M|=4Nc9FjQDFxp6X%Hj3#kWhdDg70<3&t!yy$eQ)Od)p2{E zTuX(4MPHwWaaU@REhji&tdGdXToZXu0E5p3NF%KMINi&{QQdo}hvNYCaFkjROfn`} z?}2)t)sJWkIz9Gy<@tXz^EGyp*FIob{1|`oF9ZqXTOACD;6@P?JQCm)1HmDPE(xyB KpMU*-{rNxX#87Pj literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/12-flute/60.mp3 b/src/extensions/scratch3_music/assets/instruments/12-flute/60.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0189b3effe290f8e5e4ff5a068796407859e66c2 GIT binary patch literal 45669 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 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/12-flute/72.mp3 b/src/extensions/scratch3_music/assets/instruments/12-flute/72.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..7b57139bf7fff48e0396e3853d73b6451d624357 GIT binary patch literal 33812 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 N1O^;Bn*IO({txvZX%qke literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/13-wooden-flute/60.mp3 b/src/extensions/scratch3_music/assets/instruments/13-wooden-flute/60.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..c898b844e6e3383ade3ab42b5ec33e298a03a4fb GIT binary patch literal 34383 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 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/13-wooden-flute/72.mp3 b/src/extensions/scratch3_music/assets/instruments/13-wooden-flute/72.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..6a7a9e99e9826955cdd38633defd14888cd2b479 GIT binary patch literal 36776 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 P|NXi+BjcL?zvq7d;5#Q% literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/14-bassoon/48.mp3 b/src/extensions/scratch3_music/assets/instruments/14-bassoon/48.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..5592bc64979f40a098c71e19c7be4acdf0e16ea3 GIT binary patch literal 27567 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 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/14-bassoon/60.mp3 b/src/extensions/scratch3_music/assets/instruments/14-bassoon/60.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..640f3de262fcb50e2c4ffe4b4a301c654de39052 GIT binary patch literal 28049 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 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/15-choir/60.mp3 b/src/extensions/scratch3_music/assets/instruments/15-choir/60.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..489247955c62bb7a57bc82f3f47269cdb9e3c3b4 GIT binary patch literal 31410 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�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 K`|tm6zx^LanboTR literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/15-choir/72.mp3 b/src/extensions/scratch3_music/assets/instruments/15-choir/72.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0e3074f4328c3144c5868aa8946cf4ceb720e823 GIT binary patch literal 29022 zcmd>m`8(8a^!Iya7=yvscgDUX#u~DWeb15#sm2=0u0CX&v6Fo#vSyb(RI+4^WQ!Kr z%1$B5{(fe9zTaP-f8lXmE*JB|d7am}?{lB~oYxIJN$v&y-!CgykGsUb(GkBN0D$*x z07j0WLZTR$SlBpu`2>VT#igWW6%>`#uv)tMhF8ojuHCS{jk9-hc6Imk_I>Cd96|_> zjEhf9PRq!7l3!T-tg^bcuCclORYzxc@8Iyr==kK!=h?Z1@2l&Z+dI2|4v)`3qAfFH z4HF$%NhL)eviARlB!?D+8*2cN1rm-j*#|=Z`@#P&Kfx0;F+dPONI(|=@HxdhpCt%R z0l>~b{|{cHF?c8dd3N{=^04$GKLmyET`U$0q|cDkytWz@GqDFt z{p`K5&pS85?#YZPcfZ{UqE%gg;KVJu?)w+#;ceLV=$23Q9S{UB=qF}anVA7_0HFBE z0iNJ`$pM_}wPU@vr{0Fmo69bwMy8~F z>exRak8H9=z3CXz`TK_KSn{et1PZVKpt7gc;zb0$%y=}dy>|pNDxQyHF!%Fa69X3G zXVa&;%bvyd59JFSQkPfjz@eA=7d~ba%>3YpnBUPqVG#?C+Z=EmKby2e*b{7!|txa1gPJu?U3G6o{- zO(KB|PmA1C+V(bDe8+t^_%(O4?WnQLLn~&uLuq(Xc5yf~`Tpx3h*Fi^3OiX-W$px- zhf$#4==Wcj$!m<}!Cwr?uPWp22^95&^#{$iELyvr{|)_}nW7d^O5=Ih_MjbzLCq_6 zG~21oPbckA#F5KaTCm(AM&nFO5{2fA2Y@Sb(eR(v`*1dWN?;)^J4N4$ Tf?z$yF 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

VnzO8xF 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 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/16-vibraphone/72.mp3 b/src/extensions/scratch3_music/assets/instruments/16-vibraphone/72.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..ebdd5e0e6730e38209519074f3d9decdc05b70cf GIT binary patch literal 23547 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 Ad;kCd literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/18-steel-drum/60.mp3 b/src/extensions/scratch3_music/assets/instruments/18-steel-drum/60.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e7b8585bb63251141ca3784131ed5a5e594762fd GIT binary patch literal 28015 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 Tb3h={Xb1%Q|NQ>{>*N0c;5b?; literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/19-marimba/60.mp3 b/src/extensions/scratch3_music/assets/instruments/19-marimba/60.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..54b7df80b9c086bd58b919b98001cb1ca22d1485 GIT binary patch literal 5229 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

O^5#MD0t9&he8_1_ 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 WSfaro5P2+EO)D!&$yxn>fByqAAN>{p literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/20-synth-lead/60.mp3 b/src/extensions/scratch3_music/assets/instruments/20-synth-lead/60.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0c90c3e32e8ff2bbbe59c150b0ab055c2f2e841c GIT binary patch literal 60288 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 z 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 z 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 K#Q6XJ_kRFVIG~XL literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/3-organ/60.mp3 b/src/extensions/scratch3_music/assets/instruments/3-organ/60.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..a329b732cec12fdf74629d6562b2a398a8f50c93 GIT binary patch literal 26939 zcmd?Q^;cBw`v$rv7zP+{=%I%WQ3eSKm7zmGLJ+V9khEw}YG_1aq$ET-B^5+UQbHO5 zi>95)|cNK{<%q|6z4MI}`=bxmzuePa`|%U7(glk9Fey4-d1@bY~S7#tEF z855g8NlZ!4%*xIyC@HI`s(JOMvAMOav!}0rXk_f;=dUw!3oC0ITR(p7|2;f9hGdx- zsvBv`omN)zKsNlpN0P^jpx&tiusP~EF1?ff`2YUL|6hKCheXIhumZ%@S^y9_mAERP z`jU$lY!ki1r&LLcQh$Us8iG+D5lQY86&6KPmvHjvRCdxZm|N=r}H%b~A{Rp4RFWjEvm5CGYY z&v@mXXkP$nF7mk#y{P+riNBtjWaCABefeUpul^^~(DWTm1NNf7G&H0Ud|zj0X9?f? zJXvJzp4i^Dbf3J!D**0rAkX^>`+^{NyiJjh$N{i`@E1WO%C(5*1TBw4kqKUe33dWT z&&EVq&JOrV4$1$J@L;q(yK`vOZYgS)e10+l&G@OE_v)Hf<5jmeEYByIx^Fp z2pL0vvS1TsX>G>&8Z&{|W+ymj8cpn$GZ2Vi!rEDvX3^C>NNO8va~8rqYP*jR$HRg6 z#~cssuE+W*os_Pfbj#FD4EL#;2f;@DN4KpB4_p=+f`+c%Z#6u4K3sqNB>B+#n@}qV z9;v0*pS@I_&DTyS$iy|_R|dC%J#AAz7DvWf91O1 zVcwcfmpvp8bkHm#QDAHU{x=GR3Ww`m>yX#2BBY}w7*J%LNW2hTbSijg(uD;W$x*VE zJ3{ki*dm3>MUi4dT_C*V-=0okt*px}EerbeHlNQ6e41flKXU8;$wXwE9DTrEJH30_ zymfeXs_D$H3~P}tyXmcYbI+44gWCrOSL}Tcf}sO|zt_kF00W#@K;6p8z@V5?#ih2p zIm1?SNLU+Qxks>N3{&J7qhPzxa8@=&YsI+`&|FlI5RN_HCQzwcc`49ibYJnwSFN{< zM!we}171mJ>^bl9V!vsiVgG8y#&OJp2iYK1(9v}GR0av4XQMh54lrRNWXlgpg02(av5k z5;)AUQ@>nmyRACh^ZSsCXKTw_#zXP-OOg2#)FT}EQgC!=fZZQZ{51Ljp&$0**A9~g z0H8?D0FLct5{gXmJ~+pb>q)$yv%Kc|yFh2-Y@S!e09m(Cv38UvZIDwZ$)sCB+cH;f z3Ou~h`lEIsz54sL;+ik1Z@UU{XR3_B3~lay@JqJtr}rp4Fk35!B)jF@F)&Fpk42Ak z5j7&dy|WF|zzO5rEl`Pv*Zsc@9(#2QK72uqY)*iY@L0}xTWCHP0{W>zFUN91!(jUo zmx1a&`-}4z4-HQ8JoV%Y+5*AxCtNudGds=wjB9~oN0{^AZj;kl<*B$`9|*I*M}Ks?Z?);0??;aLTKWmasmUK@N z?7Hr|-d`v7-C{J}ys;S9^-yg{aqICaPZD5+GCX?9RC-!|`)Dtq2Q(6(I@HL$F6zQxS|^@_3W>|C~x@d;`H16xo%OB89D6*K{z^N=OZSmZD#m=DjC= zj&sw` zhdm5dw`p)H)c!h^1)brq%-!EL08xtwghg|a+c1$Z?1*&6c?2^nTiQ7`7IPF}$g7}C zCOS(|j2uz%#Jjs0#Y$XuQ{s+~^i-nVZ*9jm?v1B>t>N>&sx#DM#vjBBcGd_X-ZM_e z@A%fq|Fi^OlwJZ4M)#?~vxRq{$BO`AGyoPcuZ|~@83^a3m1(qP?_ih!!xTZOKialO zw^xN34LPP69P*b;shop@%s_Hi-dd)aFNTZ&-QJ>}_1MN?ZY!y)Jjv(nNTp#)Lx{!8 zTCaqs8n1~xI?n=W)tc4#9r^_pT}>bY+z=w}p^ydNDD6UCiidEYqU>y?kN$$+w-4SJ^b;F% zuKwHR@&8(s1vWWs{tj&2y#h&EHBSmnmbYTT3eY2|p@<|iD7hc)eB#0Y}CVLDr_Yu5$-f>mN}se z!ag!Vck=vbNC7*o;E`}jE4T)NhZd_*Sf$YRA8t6!5uCiv2^_admar!8Z_*r%M!xzN z7b#i6%Jps&J;|oVd-rBxxk)G1|2}>DEtJu{=xQ+C=55hq{>rlU6B(Ao{iiaJUu=GN zhFeMI-vlS}GH*i4fZ(@J$<3}N2xJr-*@pN}AAmu^U3qgKu+5BjSo^+uEyUn;dTtu- z1n;;{I77_8b|6GKKaNW-wKMG*+i-@Vy}8}D*?|wOk1T_v^VNDz3wQem`PNv*34y{l z55VIq3#D~xAozyvyTjf81ezC-%({RR<%}O$5ZE>!1!jgP_w0nsVPXiBhzlWi1t-aI zr^_kr_W4v36>&iO>k}?54uaQVgsW-H*li20BO{wxl*toVKTh@dqe8QA9TqToT$DYr zkUjGF_(i7RLIMaL{5pF6aNGhQQd0{C#QJ&4ltXwK!z3m-ZFGwb4bZZo^nNsB=;4@= zxuQ5{!}@MEydWzF6K{ZWArg*w|<-inLSS$rp-_NyZtuAz3U$C`+2E5)~p{4ajWV-midFtM%>?A zg-g>&41+dw%ksGi-I|vWo9g4<)s7cO! zf_bpg4kM4sZjwtDANx{Mi=B+gahI*!hR@$*689b)32-wJ{d2`u?!^EIXkL0c02`?u zX5VUKq~!e4Q3N0fx^|E>ovtaRm%WoUTxc=MWGFrQQjw6_TcY?0%wpIabI+Y%=g2y& zxmJx8J`u3O2!i!xh2_)_gP@9S4uKCD3h<&E4W$qmIutb&Y031>p30T$_PIGsjtbx1 zE*sk_hZR?~Q#uAm-h|5jdR`qj{jzI%HKqLZ^Ec&je!~c#=ef@?eM?LS@4Sq-h60`0 zvg&8s%f%~g!=@*HZe_MZSkbbaIr@rL>_s+Cou$U2^;SHn983i zC9Q%h8iV6epVKa@AeI@(V628?*f^GKRs zWnp4O!^H?_E}-qX;S=A9niu^N_t{5GWHWz65ZzVZhQ7f|}vL(m8->-G9 zsETX?!50-S73`*6(Ik7kIOm0`mShu^RvXtBR4qr%Zs{uh8f%Xw;27w`0NB)2%*b%; z01~Ugk2AUYfl1*)t}Thfu;SHl3!gYCpPTDJZdxWsGE#`vRV&Wblmyr6&E($d8k^A! zT1`)E3+qDMUlvz8+CE6wh=c)v+JAhE982_+q29vr@k?vdi(WaxK*v1VKF*e~-Txwp`vY##sO_vUrRxt-~UZcT=u)4 z<&pFRs@?LQze0&GMx=ouBFMeI0o&IuZ zHRp-!1czDh(S6Ly)n-y}RA0=zcra+J+N4eu46ZF?AwS{J?ffJC^f-YuZXMpUK^ZT5 zGiZ>EVuhX3NN4-Fi2NSwOr6I8S_Pu%jl*|x#pC9jjV@fi*4QLb`FY8*v<7`dm=G+s$GYTQ zCmtjGus`L8Bq7sBI!j8Fu@7G=FhX}}>p#K)U?^b<5WdVmz^()i3oV;S#hlmaNDjtFT%MC5zfAlQ(y+Eutt-Lc%~iW=U!@B@#Zw`w1?rYuQwf7nPy{@l zsKyl(qJeK$AAeEQ#!$Y1C4cNK^W!e_lbnfBZwv!(8QRANqtnlv_$v}t7q+a;CWXoo zDZhDi?*jS8JXzO_cF@eV=d(C;DT7CkpleLc-mHDqkJplnrTZ@`4MeD}qX95!5wevV zRPI~2mT(6NglN)gd2ZPdZGO(v>+`Y}pr7sIf=JHd)g1SImUNvJYeJU=+#?wE`JUak z>8@6ed{5?wYH~1vgQiLsXq=a*-9Y+S$xuIR0-XU90?`;K9eN z7k>t-_C8fx4c;%`)cM@0P52n@_H;k}K4_El72JzktJBz9|5L^TS@5Ut#R(EM@o4u@ zN>5hTBJ6&GH4i@U;@Ak)Xlv}k^)8t&<0@n2{m&bYh$yev|GAD4?<-TjPZW%ecT|{O zyteOfB5O3PSmfbTt5&DejJ%UsYBN~@7wnyF&CWjn!vJRohL*Zv5L=y&-gZ2bdKa6s02A%-7>3{V;L#y?vkFjxs2Oo+*7+#f>KWv4> ze4ax}QC$ZBk5@hAsHYA|5L1A-BAFr$<>Vi<<0L+ra))47ihZWzCu?iD8lgXn|M*d8 zG2VcO!E{;>9lXgLm;78P9X~V^(1=6utqc`><7oL=1y1_ec<`i0mCM6FSAqY)+zrwg zRpQ~Fyzi3sdKh#lKY66Uyc>}qE;!L$8$%MM;5sBC_?~MHC|uW`)i<2t+o#jMvC$rG z>k@Tz?iyN< zhz!vA_fVX)WOc64_Me-^bOiwCW+zQ#NFhMZm7Dmp->5jhVInigBJe&%kdx{<9DqT3 zLR>^>J`t+ozu)n)rc>|=h_@dsEA}WIh$*@#dw6topWe?lyN>mJVjamc<7x1rVsQFF zDk8TuVEvPsxws#fQvh`*O@_%MHqR z&$4`}5^NZ9xx2CScbt#j{r;8_2xhH*AUEqcB|W@YRhIz1zyBGEgiughA|Dcw8+Ydx zp8*+*z)(ZNDUrvD>N*s_7!V;_;Y$)s#x8qLn*?+-(m7SjL`Ku!%&F$#H^)mbuRY3T z?H6u=jb|UD5%+yAo(dG0&+j!lR|KAHwEpMxiXbQc6pF`+@szi{&m4+{J7p9e|Jzpk zJ_{avG{1Lk@S^q6&=r^PV5qyIc@f{yW`zfscd_jwKsylIW@H|UVS>v>~wbVN1!(n>$#ZVG27Ks;#0a(L`2*d|rCyemR5)mSSoLYik`$ZVJa@j89 zB+rweC$tx+1O}p!q$uS|*Rc6k$}%-ch<+9lyM8y${5>Y8T9`^-BS|^oq~6yg{HMDSiVlc(#Q+IB>ACvGU&=BQYAu0=cVyXx@I*GKOZpqlh-?E)L+!Yx@5880 zr6Xgepa$8N{d^VFC5+!hW{2bu%Gpi>ST>T2u3bD1S$;;(;jME`!lyaL)n{FIJ!q0b z#%{I${Cy>l!{e2_86API)_1aCQ14eX?fBVOmzF|XkJO(0^(nmxNj@5ra`>@e8Oe=@ z1(R$?6=T_i1P1ILUuH%`BRb>QM7VEVHi*gNi1x%)=U>xvO}2~QWJ z{XOga7xT0rQlN}HgosUet%?9yoODPcqjWStQTsBlD__93NM|Ba{Z?)blaeD=S5 zd>RjR2{)0`Uv*}ma1lqVBY94P~WVZg)1+$$(V?0a+B*3f&gNWaWJ;nXoYx5Cf*xao zBz>WTA?e-JupJl)EF3bK{xJUN-ieOP(*rr))2|tJEexY?$~+0G16#KqzwSx)1nY-$ zWV)XxH=Sq8G=-{@qm!THGs{J~+2FEmVhJVAmLxouk;CxtQS29Y0RDli%`n+pxzF$8 zSC*14p(I9xRjBC>1>rA!6Q!m3x)l=`Z~I%V7L>dopbb7%vn;(GSuHYj>!GmoS#WLb zPm8L4Fr?q%aicAf3ypcX?b| zACvF&Ki3Uh{-5hy;+L7B`$V=oYt?o+x4DN$<@d($761D2@_#}8V8F2t&WorlhzbU; z)C495BssDEHiyJ+Bpc?zL!wD~FlXFp^mFb93}KfLXQY)fgt3kCCRc?7b5qW_uikfZ zO?S@*l)OyqwelwT;;gpl=s=O>dm-3ze?iDT*CVs?=?9!oZ_fIHN1$a9^kML~Z_=|| z%Tv5(&;l{SC?qTvO>+T8ytqz95qf|j87k{sg>7GAAr!fF%@m-kaMU8T)AcwT?8yj5 zmxb0gqb@RcG+yUw{{fqO@rsLfxv*G?BL@oqqts{{iPGv?i0$tC2Rhx5A}BcdLYI8r zZ~dfeG$E9n5ZYQTWpwBcLMuP>%ZG=u^k#Z#>w58O1UDW@l2{^WV^mb3D?{t;YSMq} zuAo65YyX=2`xipfUj_2Y49E5wz?-dq&CUzG5=%~P-m+?XHlF7X9z9^YzWGq^PD55J z2-bvQOhq#FCqm{H%vjhNibfc75_3axxFFMIoOIT0`n)su zuD&!VkTWU?q1it5Tz1~pvplb9HL1-tVX$&O&KGgY#J*d{MCha1uIJ5vn{IWd#8UU& z_vY_^PBw4dTXWA;rP*pIHFSb{r^mAlRW`&n9CgAJ&A}1}$FOLtWm~<=`Sk~vBxWK~ zpu>BVXskeWDl>ptggOka4)Y$A6@`Je=089CPPQ||KUcARWY+tf?wT#C4RP8go%wbaM1(TPQ0TB zVh!kE0Mow&jJ)_KWfmbD7uQSgo?Z5Ep`3Mj%GVSoxc*%}H>9=j_OShJ)3AY)m?i(c z3K4VWIoHyip-zLE!Lv~NqDbdyou*}>HTC-YrC3HbA>aV)K{Uzg0xyyrBAlw}ZmaKD zR+CND2Sa8UaF?rGcbJc6a+I7NXcl|%$eO=fs_ps4`@u)ABTD}C-5qLjljev*@SZst zq;jV(HnV@-%3WA&_^3aE5(^{Afk%fH8lP6Gbg6xZOLeZ-{E2UD9jT-O%a5Z+;Bb7; zyh>n^Y?V5RLbZwn)6UNd8X^;q-`l&qE~p8_;EXk83VkL+i_f(4Ox)CbaLY2P{FR0VvAN(Qj)niFKq|?Eg@%#iFf=DFfE>3#UHF95%{zh6CRUbOC z5iwA<@}9#ynW;#0!1RtEX%2@Z^uy30T6c==k`tXvUKfcPcFV+mlK7&lFMIml>U%EK z{^y8{f{rK`(l1S%M7QhBQn{|rZW!|uJP*Ks4&NJUwckzDhHOTR-md~d?HdJ_I>-cC zJeodI8_~g%o)p;4$dfwPOw7Y8v5*$h-^PT5u$O;wFy64utH3Saxfi#dIiws(7g~p| zH&5Q6!*qBHbT-#+c3gTRL1eOi@4vR0to%!JSIm%Q&e0-9$|)-w-1QDVZauE5gi3U< zvK2Z71Z?lPQis zu+7^%64u9#KIk-4oWPQmc1eZBsnor0!pp{<7aZ?~ju1=4Ql6V2Sbbg6-t&w?+Qr&n zXtX)v72WklhuFrAhGX|4G0%D=()g5}S-1tw&~3oohtfpo5;W;z0x;FoDqc|VG1MS~ zkkr_@*IAl2uj6L>&_C|4Ta*Q{J43JU~u4 z%WH5~b^T6Zzm4~$3g;dwnRp#Obs>@w1_Q_mIV8Xo9g9H6GitWYB*}LcAWUhQ>2IH7 z#d@d6f2z$>Gtz%mu%;kv;mmmI6s*%J{H6a>4fwE|MK4BLq1UJgLS``bACj)ccM9SR z-e=d}&swV;cS9O(9ju2`OHw2C6Ajt44Yo#gDlNdM1?l4{DEOAip$_UwjJ=H1!#<5B za_E#ix^EQDV}O*o7ynQD`Cga)TGU;y>1Wl4^;S*S7#QOlg+~Ni;G9Lyf$z7FZP^gCSt8pOAZJR1Ym=?sQL@{=#o| z4gPr}T1ioHu^86jE$u9IQ>{Ufe@7ck!QV2gpR-)wY5lQWCs7a9AD;4zh+=%e9Q*Pr z1#Y64VwW3;-^M?H6EjfMBHb)HmIx5 zXe_M(glER?CfBRI`#%0&>W0IwBKk&cG1>zo*IQ82Tiq#Wv0|Ge`V-e&as4 z`_NO@{Ji10kPIl8uJ4;nl!mrNn*jM=a0ED^nT3EAP)a+dIZ<+l6osUZr4Qr$UHnN{ z$eCsTk(~1^492Z!R=vsWnU{0DcG`LEr6~Sx!^>#7WDVxfCrVvcwHsq{%ruSuY0!-c z-3D(JcpIy_LO11e6 z@YQ#!WS0?%?QMdbDfOmhCPC4<;OOfN-g(QHa}n|K4leaj>>PFV*Kl(4`>6jP1E6D; zXd?%B{FkX@!-6o`N=MQ86iSiob3=J4eF}o6Sj5YVy<9n@ZG-Z?-t~7f-?C$LqJQNx z0Uphe-mo24mB3V7!^oLV4{lHWY#X{HnjaUCR);(1zJc4XJFF+?mHYy{PaB_yqNs1L z<##7i*Cd)L9Oyjzidhp8HXQo)j07VBt#S9fH=q&u09C&1y5DRHj;HH&p+D)IX$G@BRj6rP1mF7!#}(}b|?y zHnEEvz|=;aGTw5SFUJjU>#A+h71@ccEdJJ4~`U|q#;N>lrBl#qXo^H zq|7|TURwQQrbfzPd?8+HjBBY}cF!q3>*#mNX@$2M*&`lqPZ2hZ^I$snzRb@G*NpKS zuk3vltBxQs>Q?zeLispf&`6Lh`rnci9EgpLlBsmnp0C9&?G6}uQTuLarZf>8hBxyY z#-8*?9!V5p+hIbh(S6mufjuG`XRbAdwZE+Wl#$t1q|Ij&cdq}{qq)%@2jWLP%}rvk zwO%B$T&HN@`?^$6N}F@-zsYsqroH=+8~4<_lpZ)=hTA4MM*KF_aq9oLZAdyPRdP}#QF0!jgJuJQ=`fIPs}bQ zm-V=Ee%NSgHL+;sOFiWTLLGeO0vfE4k=Tmr5t#D~oN#4n@_5G=uy8-CN;?|_M`9b3 zM3$NJ@kCgHbI3DZ>kwf6{X6S1=z*yTY9)8$2 zVSIGWG|zi-iImNRfd6GY@x7FJI;eG?aFq{6FIr;Dqn7YnXHO?+XK4Sm2$@zmVpMO{xyXTn5kG4+nKNL=j zFQv#_lNlVuYD;s=bB;X=KX7JU#KMIHzb{?O3kEMzGVaJnQTjxi4v*BJ39?EEQIZXy zr8*S>#7#k&x}`rqF1LJsTL1hWN|<3llhzQ<8ZY$FkVr#8DKS2^J;svNvEO$R!Kns0 zp@Sl(Q}^ygtXk!a?L=9qI}WnMn42ZR%;z~L?Z_e*y{?0c&dX#`y56$QU$4FDkIo(N zM{v}*@~<9lJH8XQfll!2dbtD%2}1z_rWhoKtUezy_jz=AgfvtDpKDx7Cq8;acRgG1 zcVbp--@02HW7cAQc+P;_`yLe2ZI?8A=^sk62kdousPKiLZI*}q>JeZQD+$_eOLd~$cf{&z5VG-I$7 zy_5nY+F=2iP0Xa{(g1=p-u?CvE~hPDB(c$*Y2&66ja%pCAGiGnVolC%w8;;WPRGu^ zyD@qtpXYKJ-$jk%U+0wO9(bR=34&jeYIMDZpn1Z;(PmA&uZdP@cBTbp)3|KH?}jf8`R%Eghw(Eq_z+$RXGpus^N zG_H$xM&^}n-EB#uJS&Ln;(UnW-5+@Fk>Pn>=$^$*jDtpE61S5z2yUDIn2>ET(^5pO zJV)q)xV~J0BYOp3SC_l|k;0v45FHYJYm{rzR3V_!S%r?gk5g+sob(m` zAz6L8Q#V1=oZif1tJ8Sd4`kVKahnCxrv+Ghw)X=6n6;6&LQ}w|MW@90Jpq9y= zj)R?ZGs!B{^RbpsXi?;aIU%mRDKC827OYzc{dg>za1Tja z)zicsS`=O6n+<|fr>%13VVuj*_~@V%{P(b1H1?TDe%ku3E(D0j2a&S3Xhtjm5*7`d z7(+_WaTs!*IC0%l-I-E57{MaZqQwAF_ep805wK*&*r-$GzC^(R%72kv>u~*Hc6y-f z-GYBn&vjB0$s50S*M4sHMKVS>e&p8JVfC)_hsM`wv2PPrAILhq3~n80*cB{Mo!DR4 zy)U>v%YW}{i9={Nq-`CV11UuYj@wb6@kWR+^dzY{Jp-P?1&d$)oYW81bH1TX zOLyX9ODf>jun^Tu0(oHW5PX-L648#&`Xv4pLH0=cc=o0gI4!j{1 z!OwOimX5sq@D2$if5m&$f4kD3>6w6i6Gtcko#_Yh`Eo zdnIYz`N{RtVj%=G0~M}t0Ao0`&Im>JGpu9XfVvl*&q(nN?sElkvs>MVGV~l0PN(YL z23|UvS7Ul4YY&M78=7WYc+F1#8t3_x(sSnT*}HZq=&$SJ1|Nx~%;5kwI22t;43NWN2=`RloIkgI)fq z)%tSMJ!ezIg6WM2y9{6O&+kt%wOuu-*?U0$m`bB`w<(ApZ1kbF&i>26pp z1-V!ukeX^UtA?y*nIQ~==hkj6n$P_xCCzYY%G4ia(3%pOfU$wLcCxh zXd<9HE=vi&EK88MrDP*Z@S``B1ISpnAe(QnuFeZuSh5mEYA^7{O8a2tE>o4*>rV?? z^@yvMZ@R2?Rd&Ww|(I+Tx4g52?o=KF*^scEsZV$G-4_WhW zpT87|8l(W3VKYpw5-MEkAmo{zyGE2^1JHlyP9y>-bcEBjf;Pd5(@@XVikVxZv^bI+ z@9@m&Uf05(s31!i@U-{FYmwT~WS6y}rA}X41NrioZ3<*ZzSg2^;vp;%x(D%c;C`z~ zKLn7_gu!Wf$~RghV=d{sZ>D8V#8|F6C0WllL~q z)dhGaAX&;2kUH-wptUj;?F56}ni!B^NSclE3Bgzln>STuGk~TKD=! zaS$qk;3gM`yh5(_g-{v-jJ8&Ok)39MlO=xTo}n5HTfi-P(v%fYxlkZW(#dAJPIW2^ zU<6HcjkmPtVX>LxGF?C`Nh4Aot91A1j&(4Kwir;u*1lrd}|#2j(CPhg#x zj*K?Y<9SuRaqi{ME5~E^&A4s9OkXrOpE@R+E}IR4U;olGv#|gK5*E)y7YpYb%y||E z)N-SVrysSq>(Cc?yV#NqT#pRyER57{rp;?bNs9WBkm(5N+9)H-B<9i^*OBj{xHrzt z6!fFi>uDWS_pBcW57oECZkJ~}o@Jkg^xUYIZ`6lq@LLMr1c{_;r%})2q3VMMFu$qQ zXvF*@>Pkd!Ggl~_9L^{Rk$w{jUHV}a1L9?WK{1fK` z1m(U1hdMAibItR9qsX;hbTP(J`}B-g>Haj^3lH9^pvJO$Us@sQF>a4hFdQCFTYH8i z(t$5&XHH(R^6N6WOG~4*;OvT)QSKC}AhOjRhj6Pll%$1saLU@qGD1@8C5QrizNMEBBAMoZ*|fqh}6kn-1iX=au`^obc?q%KH2|i&zt+p zwT4IOuaVPfjuFQ=i=+26Zc1cB#@4jfgX6`tH6ywT*Q<7mJ;Bh5)RlY7L^KBK=o}&l zm-PuIqJ%CMwdPsO$rxZnuRWNxgdX#7L73RD;BxL2OQfpDg}o?{OkKgyFiQz@g~3S0@8EQt*5$-kYo0G}VVvE4-YW}j<{zqo;DE#!yBWVD z9?y*h1oluO*ff4-9A5dkktna7L;;92VS(%ahdd_$Opd2a<+|{EWV{&FsqZH3PldRQ zDO&JeX|Pu4S{C|Hw*)O31=t{eTJ+4d7SXAeh#{P>txVo=D}$SK1_?bpUP-mw4E+2% z_vN%=6=dP!d9Q&df&?x$sFI4LLBh#t$N>4tOhPh&9#(|9U<>m|))Rjq0hq6M?2O2* z-Ax;PLN>R0eIRipn-(#(WR%ruA#r60d2*0br@9otZ; zo{P!(5DGUhKsbnI3-6%ND`m(D@lk`64lum@0NQ}T3wpM}}%!SHZ!Es25^Ev5ghBDgg zx~dKlofgi3)~EPE&6pWBJ?8LY3Dj$a^doVH^IgfQ`!{9(t-;WEuc4vG;|g@aYX+MI z9e0=yx4<>19@{?n*g!OaN^Fz8Xy^?a>;s~^GuJ+;w#!u6ka5(TDLLrnH))F~Z^twv zPNiER?{_S&lmBmnzz^C;TMnC#gN#tQi2O$0X6EQz6|gU`W=~Fz*3(xEfAzOvFv1)H%(*?w0@ z@CNwLuGafo)c3Cs#i1U;LEyMi87W5yBLvM;gsAv}BxCfTKKlk7YRMLsLc{< z)?R!0K^~D2j#ja5Qdb9pW9sw?(GO)UVvb00R|^BzxNJK39WMR3{AWkD{?l_CN=oWJ zsU_(AVRFiEefHp?;3M~(?c+&sOCzXavmzS`N^bEv4MP!rX}U*rC=_e_%Afw8JAOoM zMvfp6rIRL8OIBj`oQ1_{cfNF~-27_sO8yM~&1vbz!cEf1e9`Y-85= zCNu(r2(f7H1a{z&3El7LT+SJ?he{in(uz4=qJLr7W-?$9-+_A|`_Gm+^agi@lqB?Y z)X2UQO1y{qF#=;}keG9)ho*IzfZe_J1wKr_5za83%bKJg@6WAc& z*tBZ~2gMo+Uq64iG=0;8J917e7G!?}g03IcA18jP^|5{d-3AXu@_GE}fRP-C63UXv zEZ7)IH}{0FI5JmA5Q|My$3_ymGsoPuW*!+^EPn1|YI|k;{oH1`d(SqpuD;yE`9a{( zn)ujoa|0L${<}TMA*LO`2OhcJJQDYMeETD$*rD!nzr{NQ7Sf0Mu#J)6DQmnEn(NF2 zd7-zNc3ig2K4tfw?l+^b>`(*v<|OLnk)cYxgANbvyB9s8TN)?Q z@nV02YL4nNr;6WTL(PW3k^NuW+YGfiL{kF&qZ8mjprgdotDhn{PAhyrmmLW4&VEzQ zdodhvTj({%pRy88L>gKmAPUC?#N8SaGLWbI@>!g(iQHfbyNXha#v8gw%(&>AV10gV zD30p2m51j>@>+mn@?st5B()$fEh!y2A-t9f$_6d7piqbb9e5|w z!~c$Ro)Dmg_mQ9>;FaY8EC<4@Ve_fuSj9JfY->30|4@XX7FuRmzG#~c?IA(WfBv>; z?=YbKQe-C5w{E@mY|HGhWK%qH7Sm-V%C_xVC#f9|4J8x@e}}9IeuVfxG_2IEl|#ry zb<&2*b>N7sz13K}o}V%zf}Kf_k3ggTVLGI?m+sP3Ix@+)KbvlUky}f=n`tK}+AlKB|64XiD5cS%NIw^|O`1Ze0 ze7p(mJ*?Q-`o$M{L!FHF=3g$`6bVXiEOEpN9_{t56iGSVV;kUV-J-*G<^8bMTioph z7#F|i`1Lo}vH}IZYkho+^K3QG82Zim0EZ4A3I?xQAKGQxC)N0?K?UchX?b}B`9w+N z<8(JYsc4~44pKXuMWpkV2zjXSozD=Lmj>=p#Xs8}Ap>?h{YbMgxT@-v;ADwYGBu7- zkB(El3&&LsZqFVRrch9ZYtV|$!9lkj`(qhsa|e3N^znf0{Iun%nf`QMCvRW%oEOsY+t*hibR$iVV;1QxOOIzxxrXZ3YD&tPv*_ z$SYFQ`Slv-#C=P5>)3_Fqe$`h`GZes3Al$p9-DmtLfw;^IAzPPGY7YAQ}dyg&EbrzdtsuR}Kc@T1#u91}3!KKT?EJt8_ap^|1kqR5^6>K%RF1kWqv4VOfJ z{Tk=;B!v~B>hRMQ`yOJR)I29BNReK zJT!upmAdj&BP<+~F_1V@Gqe7Up;NF6X;ktuxS{1f(|g-JBFz}(_tch3rq9eU`(by{ zkvqbA5E8wvasSb;c=#zYk<7e{Nq@EKNka_lwWyBo>wsYl1>3PaXEiyb(od&zik>%ac zs9A0y9e$@PU#WV|Bo9pBM+A`o_BVrT?vCl`QfH#1Ht3W;y)*d2kkO9D zWua)fgzi>)_aJG>d(`I5_?b7Hw41u##uC63SDsz4U~k`~o@hv0>xQC|b_i3a(N$IN zg4@ux&Q~+XywW!}?SjF7DUfYka$Z`Hu#*3WmvNUPWGcUDP2MrJn^$gsdRgsh{i`zTk zsy*u|`Im{;WG8pa9j{{ef{#DANVe1oUZg}d;ULz0{34~LA1c-Lpx6Bk>XlGw_2knq z*K2g-GM?<=6N*Rq(aMakKUhe~DRHbYX;7U#6*1!+NfwDxxuZ7z8>?^m%=;h`zcbzTq zJeCE=OAk#`seFtfHbS}5E@HluvQ!G;{{W5|9>?_Rl&qetx>lq)THRJWS?80T_Uk8m zU4vDIc$Oo}_N!vi4BDsteG<^+9i7*lUmWHUdrTMvF^;6II;od_5OJ@Wj)$l%^dQQm zr!KTNBo9kF8mXrjPgECkvZUDSh>*3_Ev4Eop7HLak9aI?eNLlRfa%K_!r|W*&U~0V zEC0y^d;j!nFU%!QNO2^Vd#3N~>ih2(X)LCN!I%|952u$~-7fa68L;n*evj+@iFLbSs@HG(OM3h-x-mPIr@6!b(VTKgCol#9C{1=cm~J*^ye- z<8-g0UV`joMq-34wR1*_7gre63s5>NeWYB1w$5*d3!Aqv^|B}&nz7{XdZ5$%-mLe$Kv`zQS_J+W zm{Yf}aGk=p#fV8 zf>z#y72dcC)T(lJ7U9hU+Y7sv%DUsuZ1?2zkw3?=vZ{V8oiwL?Nxd`}m*49bg@4PQ z%bCQw%w|8VFIF!qX326(0n^v8kTIf1V3FOAfo`3)wxb2<2e^M?GMlXMpMQZ-SQMk> zHXwib1S(2HK`YVE8KTR}QKN5%x(^%B$t1up5nREN~clqFrN|hnr zJ3w4K8Chznb$Zoqv-8J?4;k0t<&4?pzmjR`Ag$7;enk_iVAFmVgZnpLS-g&130Z&+ zZ@T?>RM>(Y=JW~Zn)oufs=d0sWzz5b_yeuLFVlHa%7KC>0#r4L1Xgh626EX zoVek*M$~N4&D0yVey3(|_1$yQ&sx^ri!X;F_h!0x<)18#_+3*0)A_qpcYZ8=G4QP3 z%>xgb+SVrz!0nt@yB8uuAw`m~hhWyGz=h2Lltx+y0r>i8xZ4SL8qNTtlGW1zo$=)X zWhnmZ|}h0dxKJw_l90TJQrwKg@SIs{+~UT9;J61h<=T!Qw3_> zrN%!)n+2#4--dulc)G z2iNGoF9n`3uvYF5$u9d4(J6DzCl+9#;DcQUuk4=9CSi!za>y4|mnPvH4Tb+B=wJZL z3o4YqI6>P_%2wycTN$C+HbjZ|8SX(B8?D?X?oYgA{#g{xjON3ItBD1j-s%-!4)`{r zfpv@eBSpsUKZj|XwENp6yEp3@Zwi;CIjow|S3s`{@KR^K>O3_zCfVh~)=99+Ff^pa z*P-NM%b0%>RQH)G_xnrLz)=99_HM#oOB|kO1jtJeXEP*!D!Ntsf=KM1WqrcgaK>k* z(w^a0{_+DAQVB)a2yu!iB=G&Kh1=~({?$}np$*1(4#UIT7VWM$x`(=)#kc3|vNb{L zb@rPVy_H8aAD_8N>XL@k*rrtJnY_~{4yb|p2Z@1`fo2NzS$XPe6!qv5m?);LP?GS= zb+HUvwhTeX7=mI?V_`=y*{gZQki?s~?VO$Fkymmy++YGxJ_J-obUPp>?#XIob~XMR zvmAXywz%+QR^i?;N+#^x+*ZEk>WZKEB!a~Ki>0-%MFRQ&`yZ)3Ud8iKoOrDCQXQI? z>8zs_ zo|xCDpl>8!`0(TqsA$)`(dWGrb&XHEW?fq`wpJIgjt!O91mA(*pbbkhXCY7z=6pYo zE5e>FYR!ljsz-jy6)@8-y?nz) zU+kB7^@qy-bS>lff(c)>5=g5X>W>@evO0{*xW0J>nzWkH>?^8g3M+(G7@_{6(1qQz zW)4TtF#zZ^hzI%`Mi_ufzxi%rL0+&(MA>HnbmO%FoE!PZPhxHCorWqY^LHoB&MZfJ z6ykoTzlD8o99j7L^ZK>jnbvc^+@N~asdWwU4TP2L*K<%yYj!yPF3#vtB6QbajeYW| zZN*6zidP}5t(L^z+q-NQR=4>`id(|VlABJRE*d|r*7$~uWhUDFiZYZ6QFugxhZR>x z4n-1XxpjZ%>SAuGdjE5hv6l=txe`(Ux%S^pMJY_t`(>rqZs(`Rh?o z<)C0pqGe1VGHf2yBOlgxYh3Wiy<^%~e=x2=%JwL{wG?u*Q7`e;R`Sp^A~XMn>rT33 z%b#L$w&lp{O3mbeLtXjM%^t0UarsWeftDSnwlzp_rFUE+-WJy=(tM*H1wI&9Sb@x(3UumiYmbqLd#E zS$l7PPGAiY=%Ig0TD0w@>}UEKR+yC&mHwU(2{gL$8soV{{UNv<)Y{S^AAyPhSfr_P zXFYr&T8yVsy-q|wSzl(1=?1;PRC~L$fFY?L==63?$Rl^SPcZacO0&4=@z?9E1Y*87 z{R?Y>X2eE(X1~hi7`YU<g^lKMx`_=>OrnM}$`l!L7I+(VFzYZuKGV&pfM8DeQ1n z$Ik-bR7I3T(7}HuIH7vGKRnnF5;3*yqAcm4ueKYX(};zS*A*oC%SnA7q*qD1WBvX~ z_^8EN(S%dtv6k>3ETWn__!2x%cTblVluMm})d%z6fqvW2f8MCmW#xi}c<4ti75mmH zAvtNCLq0}T^^7Q;2vMHQ!QOXDq6-PPpRB83pAv5!YWMmPWBP*r$R7CslgR+tBN~1- z=Gab>m)L?bKC|q$xs@n2aK6{0P=xG|F0!@C9rLpg(B=;XX)7mcB2CU6`ptph-;p)MgAczjBsxA=@r4AO9v?Ew=H|6@bU$XXd&)v*g-lt^y} z=nE!Rt7%<9egTP&9a#>&LL!%-!BJf3yQT(t*N?*c<=HiiyEe8HI`*zJp=!|W%``ac zRduF7bpg`B5p-}03l_@nyepkEpmjSV`fezvVr)zmNnTM;uSY`j0i^zIp*|9%C74(a}eus0w;?4mSqEs0zoM=vx%1Vxzq2EO)GoeR&wP#3~{LE{*5h zJ5BtcY5t1V6$0MAlc|lxOzeelPeqV`y3b>YZ%G z)Hq#&z=<*2uZSNKZTn(iiGcTv8c*S70rX;L&OfcUWK!mm!j1-x|3Q*?UMIFwc0_?iyMdQ|h#y?yKnP1P_fq@# z#sL>4g#z?77}gf*kxs}MF3rnGQXA<=DHCZ#_8o z;SY9NGs{$F^YCBF&y|lAiVG?tQ3NC$ck+3)d!p9cq=(J@g*T7g%QQ2G7H;g%?KQYh zr%5`eKx`_Ww@B`k?m`14Wxw$*7mHDtwrdy9YzN_dLSk&VH~IHuIhFW3Ng(YARBj67 zlw_)Z69@$WromM)|mwB?I%tuTm$?=ZL&&8-yXpZ*(6Q=eONN2r@sn?T=e{#{32pE-PGKu*m^u zJiWogFIy5inb0Z!#-Ba9U2nKnL>3VMP=8_&^bMVxdATVX4mQP9C|`w`3IK}unysNKbZEmREkCV61Bx_UHT+&)vq&hom_B#dXe`^@q6O~mo0WWiRHQ^tS1S+o^P%X$2;j#_ zSf-%xT1mZ36c(YUQ4<~=jplCTyQ{_@sN^d|NiDT}n&!97aZ4cFHaSnbNxXqKLX^J! zm%{P5c=8@v0e&*9KlCdvIAJBTqj~w7U7}+!q%*;}rQ(2f_K}hTKvHWYgpU&;=g8=c z0-XVEUq6z(qOnmAeq(?fkz0JJvXii1v6AWiY?L6wb%B}4LP1ESaUOHs^kY*0?WAz{!cBaIUi}i17Fb2lQcMC7j<~KzW;6m<+R)zWqs?srCWpPz4+|* z?<@R)UTN>Yau)fFBx})=C825zh7)H(hiA`bN4vl(K9MC+?Cerhc!GYn9(hOA{Ti42 z4e9S*cYhscEAQsilwnFpSn_rJHZ{_#Hht?XIz(w#FOdMe@9=H<$^`ydDZN&lx${KW z!(t~CvQ*FPLN<@8Vu~O%z6&zBIFpNi6~4)J$%M*x|BWg4lqfyvw_NP5TL~J7-C?_D zomoqP#x^gH@?=Qrs@MG546pl605~ltpQ>Bx^rSeec>FH<-mt*aVQ06yrl2nL!|uuf zbhF?YTEm;*4A6aE%kk(>@aEz9Gg9@ug4%D)=#T*VUZfNQjwC=pWFI=YnPqvO)~R4~ z_dv5F9vOwlg=qT=h7|4HLLWc9xcKobf&jnOZ^;yMfiTdl5Z~@zus_Ph^vR=LSt2># zs13?ln-0xS{sAw{1>UZFe17HVw8QzegzaOvUf-lZk*aWtFf^9HJ1Zp zWG;$%b~T_-mRj(yrT;xs^rbYb(L*ZS|9_Q{xW55PPoNq@0cQCqaz6Mx_VO>%J-=+= zb77i~P|Bxb*2xCe7?D*YK|v3>7`*sX^;{2eUWjuJEKIjSDZTe#V6dw-5G@ZTf1 zHAT;B83uIAKQ4hae|*B8`faaSc0Le=MC0JXw7wWbL4|@-DRO{DOZ$IG4-!bcdtK(N zr*TnA(u&e+b7}8IZk^r>t?GOt+_(yM=V;y@JQ9H_xo&i?MCsXqE}QdAF^?(_XLq{Q zhuhU$e(M;IsuILzPR5BD-#h;m!6{p(3#)$_y%Nbf#(B;KpJu7^KR_rKrAk{X^g zUS*Q_Q^D2wx;%m89AK9IYPR((LmI<3<|<1%Z!~5?!$z z2zH2u{k7Q4%DB9UPgZ=3 zvXjJRAUc@1!}IOa{zc;(k_rW5t~U7NA|%!+Y3^s2gTOUgj5@@3}vPUL>MJ)P1c^)54P>kGV zJjTF)$*fgf+WV?PN4*%RzHIcPB*QGOGJ!+pHlb0DV{f|nQGR1XcjN8Fe`eBN|6Z%? z2-=vTf3u#wJ-3sY8Vb7Fm0X6&OYT7z3?A;OSBpqDko%#XBtS46;)M0WEPe%F#A@bI~(%$8L{`3G&)HjuXPvDwx90yvoj;DJ*O22}Zs&zD8qej^0A zSWuK+17_)f6J>9GYZ|jG(^>Z^shGa^)eg7|Ki?dIiUSgFMarJ_k6wHNHT{#E#P%*! z+3cmI&!69+bLnsz69LIlSo3pmIVU6|NBNk#W{hLa>W+~`n8n^yKgO)=Lnycn_C@P4 zbQaOSj%n8;>|h12OUGXeaJ{NSPe|RczKw*3d2+CDQD+~I;U7F#<-h90p)B^Kx4~~x zxlVQfJS>0OLfehBy3Z<`5K3#3lZessTwryPIbYCn>Tp17=TC7(BY_Z;ch6pO$h zkXt6fSz9x?`fV(k@=Z6{^z)dh7WjVdLi~w`sdAl{zzHZ z$yfF7@+PN*!GFE+=?PWL#!I_xkoCM$z-?b-48sw~oRoG7@XwV`$n+YZph{7I2imf+ zC(?Hn18ja53NuWe?dK#l7f^r`NgFO)4$LTqw#c))XAEtN4$FPy95%-+yf_E2ygJQ3 z3!#v3;JGWCw5rz)A#>1t8uUZU@O z4WqHpl1-%z95j`F)Xg-tgg9}Yx<${h?v2BjEtwIR>N!o+o&M8-wgKktfma&K*!Pol za|V{ij2}9BkEF(`xLYeWR^M!5P7Wphx%4nie5~#SxW)WO@WwY6iCBhcg|_6c`{Bpi z7A~$-#^Ns={`Q&tyC>Oc+>_S>K3@I=lg{>7Ly?2 z6%=xgKt%wFs{grPLJPL+5E_P%H?YUo-k!g~OR36z)UH~^Psbue;fs*islQO;u;B!g zJC-)(&C74fFmk-3jD56-OO6g!++xpgVYA}h1AM|Kyo(J$b3c#z7K zvgw`oRuS@(kUw1DEPOL)6!FmYh!+UCC=sl|XYJpKH2DSvBsT#W#fj=PNY+3cfyxTC zbZ^Q%d9z(ql2o=Tsv&$kb&_% z1F`WyvaB>8akZRfNC=!NNxGtWQy(sTrUI<=c7>+}1xHfdb&~@T{M%CLu7F7vxfS z%OlB%9?oZM8K!iq&OZ%8bLn-Belr{>?-;joZfdG*@&&(mWO)6iT(wT|@ED^OxMlO! zze2!#bz@`Dg>`)*?X0oUT-9@l3;frZLP1(Qw}ux*kXw)4sStXrvHn5YcmK~)F=ml6 zPhC*-t$_drlUoAH4`wvRc+v0`jRd5%lHV4lQ5WS5IwiU6z~B2bxk#-UN@~F9WCgq z6bzXR7^3WWE8*!LBX>5gZI(4z1ff=NdQq`m;UU-9jY(#k^vDp^4_a$kJqg*T_%)z>c_ zLNa>~8*yjAS#&=op!5LPPB@MzL_$c$kMt#wGSeCX0fwULhsrEmFX}7i)@LHWN^44L zi+XdCIITb3)!Ky%0Z*T0nvaul0`_f_x`tlNCfES0xn-}1^REtruW0g+Hkov^OQ+DD+GC@h@+12qR*wlmy+S6e?~9h+_b z_=}a8QtXVa#`od#L(P&(Qv-Y9LIx|eczwet7BjR@sZs86V8yO!U0Yc!{7qLU%{Nm3 zlsdjo!36geV%CH$SDqCLLu)tsCgFA_ie}@b^aQ8El;{Rs7*)GnU;GXy@M3-2>P8kk z{Zo=PTY0OjLdnhSBKh18H?$b5PEceRIqw_AksagKCCw1u1jZ!4DUaH-TLiThxMNBd z4l^S;I2NZ{%CNfJbqTr~p%BaK*}4ryz^2g|uYd(q{ve&4aHurf^NTDIn`iOPUkqEj zpTb}X3Q7Y(f5w87aKC2DFDulxm$pY9fr_JGUKPkZ^$=ZsF7FZ4%rqtM8au4r`B;!+ z+`Q`EBcpW-;KQdOVz#hGN-b|@tl4Jh+Q*NysJ%7(WC|CtT9-+Z7nl#J?oLhGRMja?$ z@pBynsr}}ss|uT5b8omQwo_^c;tq63=B%(srK!0}kV`HALk3Jk?|FLo3Er7-JUXlV zSGm5MLd-eMh&3u+0t?{&F9xCeqh+70xI^>rWrwZ(tpE(mfMOV3pB=j2ug8e;HzTfk zelvY(9Xs&E=iEzOsoxTI{thK!G3GbLtL>Qcx+#J;^ZvE}l!U)PrRm!JuAeNv1WuNU zbXOXnUOl%9+MJBff#Of<38!Lm*&l{ULEsqB4z(}I&f2k22~4Qh!vS7=l7y7g)029) zH*|j?c^yiu@*3~@KSVb^=~;|MtLE^|Xqum@#Jv$=cy7IapCm=Oj>3X_zWd%6FZ$ba z=b(yT$_+vSPH`kC-#*six&YdfFOVJ}>EJ?dn57?tlg>XFSC9)N@*(-~Zj0J*N#+z$ z^;{Lcu}c@n27rzKno9siEk|~<|Gb13gRz14%^w69K zu_gnSclY5JhdQl+8D%n4?! zcEFp@G+qwogZ0xohwSukMC193pF`($&(;lsw_qYOOeo8TX z8fLJ%>zF>{xLk=b%zn8RO3Tp{xk#9b*DcF6<&S}wUtd&oP1Ko%;4pSJ1@D`jw@Ggq zuDu-w4@lkg6yy6jQco1m6H61nYa7+b6MGCX3{!YifXbY&ZeQfdm zuWFc$<`Xz}S$wC0z1>v?(N;+)<&scY)q-jmq1!VXE$@TPUv8BzWF6@rShEXS5g)GM zzf9Zbw=q^!urM6U{ze!1Nr|zhDXqS<{Ju|r8frP+Vu$FgPpe*Ls`|fg4mu%URRtbk ayOHpA=sRQ?qeKhA=)FcSF?tEn3DJXSN%WG21R+Qe zopHbSe7^7V%kwWhvzD^%d#$t2Zr8r{K4*Zd^kK;V{V{cY;tW2c0Dt@;5SJYY9w9M| zoSKfFk(rf)o0nfuSWH6d-hDZFMP*fWO=%`4D z+?Vmi>-s-il4Ro|lvjX2*RcDrcr`Zhe_s5*@)x*5{O2I(4J`!1u_5mISo#3eo!L!I zzp20~rCz|*z zVK@K)@A&q?-@tz44))JQY{>gD2ylfQhC;}DV|1))Anf5)0+{+5KTsj4--+l5jA)Mb zoJ|x?UBBek;*c4gt;1#E&L7XIK>&B8HmxT(azk}x-X&P$XJKPwrA_Dj_|i;v%1qN6 zSx_amIcHSlMwg|{*k${Qe@nFs4j*v&w^r+$;lEZB`+V!n#B?vNP5+W1yQKM*9I^?jkUTX0x^55ICZ8><7*hI3uwk3SH$T-_VYS zp3mS){e_jYcWaNSGJL@rfgGClj7F)!R%`w9%k9t}{^w~P|8Q@G*LQL}ot?~Y3^Jv@ z^(Ve%dBbQ>v{n#hl_JjS-c0{9f6VnBlg`Ttd<_Gc12bWv!ysmj6j+=yQ9%adQAO8- z=@Q^mc;^&-T9XCS7~$W0TM69#_`0Ch6zn(ndp7}6ZI!_iUj-V}?(@-4X zpu{+28Yikc3A=tFKc&-9;h$I!59iPo5!B;M-=L1QA+%}wy*%gewubwrvw(dl#JhQZ zik$O>?b_aqd4*%2QA?UZ|8>*fvA&@r#nA|t9s93!pvaT+vh)@b`(y|l0#Qm$jYU|S z5=sV?^9za&qdcZ%uttDIq-LEdHLUHfkFfH#_Y7P&4JvE1;klmj3$oQqnDNh3o8i3e z2qm9b2n29g{hZy;bWq{c&l$7Apd2aZETa6F*6UfB#$fGT@S{I5=+2~Or-f?vhn5bXE=V1 zS0$egp9x@`|2a1R*XtIWyca_|b@HG@%m$SrHUc0N$vIUnZ2c4;)JyBxqTerS>e?Po z8$9I8eFqJDfHeYDL~6~yhYjmm>m7x^YI};G=OPYdJF3yTM@;-yZ)g=^#}9tUT1BSb zo1_E6gZgE^N(gsPSR3I0k6S_RK?_jotNjx?lc1wa-Zsdhl#%ngzYd@f>N$B30}>Z#t_r!+y>l z7|e2=@J=U7zEFlND9Z4eM}GA`op>Y(+_)^vmh%1u_W%GtO$YG@Mg}F|P(6dG97#5b zyJF(%dJ;Zt3ez-3IJSoo&=k^Wwpg-Y)B4Xd$VIY11%<$s~JU zb{T05***)^UbCs=VWGNDsapB*-H>*n^?8?g%bnw%_B&WfDqeaC1>Pu8N>J%G=!ho% zW-C@7LI^CmS!tiU!#4F6uYXV5^}DF#cR5gTF&Ob2y9v5<>mJu+X00~rZ2JjVp4*H86WLfInet4Spqk9-=$U(~K*B?C9# z2ip+X5Hnb#RCIP6LA7#Ch$Xg57h9B-I->4 zo0XZU`&yV`&^oxLJ2YwN#rO*`0-R8eHR{@bu?;+j3!e2(w*^8=7R%?TKMn*$$#G>p*l?jCIyG(Tx7#jX2 zS-7sj3I0-UvM9(^bCPEUiE)o=NGsGHnp$GN^0X(jPPVTa4`;}mHu-1n%Q{ZtsI9Qh~=Ph*I;%ixw z;o15_aK}NtFuXalqwB6H;a^g!H2W`vqQ0*7X=#gz%kw=|s2I{gzbO&k4TXiLa*1l4 zcVb|l^sY?AJbnV-aKo|I#EfVQl%YU3;sPYqL~^whE7x-oC_Em;Ekc#Y95q-YkVTLM zHQ#U8)LLK3;o<)v^&FZ`*hF;s>R~&r-2I);%1#UZ%zPa=EI2V&Tt#_i@h(>PX+wAV zQ+4|v9@hH$^Leb5nH-^M_YRo@zA5m>zmEQ%_U0DQ@9X#bF&MTt-?ggQA&_8P4+uMn z{T*03xgZ0jzHR7EIj-Y`23z8su0G%C3kj7-BwNPlydqD4v5bqG(BAVg`guR!0=jX= zi#N)rUy+%znI@NgzRtN)`s(J3Gkq3WSr)g|P3vqQeoQS8s`T$ZY0hZ>EEz&abQ`T>o>HR_QE$aD2&WQJL6W0e;f8Y#+SXY1=XuhiSyp82*68`-o_vV#-} zT}Q3;HyK+}Q@tLhWsWSqqZ%GGAVlwjH~=)BW>cH!@)gVc=Crani=weSc$$Lolg=){{6kUOy0=P7kqbh zWuZ!NwCM-Db_=;lLm-ubkbsUCQ7b)er~GVt1>JwFlFxF=V7A1iYn`m4Ib1SAr_oLC zt)PX75qRKMZe?|Gw4M?^>%1^Xoj-f0wfYlQ9?2(q;lFV#PNem1bTGe>Mugd?_#i z_b}7jxsRm?hf2n96eA@5lf<;QdUQN;YB2Ae2DLhiC>8W)Y{s~%V4s6U$@0-FM`3nK zJ%N{bBXoPAUpe#p%PN$|ssRAfM?SduTBiu2DMmebyQOG~Ru5~G?-o%p)Mr;49{zTQc_ z_k=`Q-fynYFI(a+L}^}My<+5nsWJJB0c7|_$-^6&q-#MNj3i?3+sca&6DdqT0L&&L z${sL8q}8Nx8mTKK(oMP0lBB}hUT_f@G>P&_&L}5LBX*P?sl)kh``-7O7kDX07c4<3 zU%;G~#GennO=TKYsdBE=A^1-EiJHlljJVK)(yWXLQm=uT56$ zR#+bjxnBOqGw@CmxSk_|ezVa~MMaj+VVz2XLw24b_S$jRwlbrNf2zTQ+)#&{##mvx zIa95bq9$?iF=|`k+XT|RcpxUw-dH>1Eh5UHAgA?R(RsUJ>%>^vYdxmu0gBIRw?%gZ zb1?lGORE@aPr$yz;?Ikg_jml`-vYqyX^)xeD=m#TY!Lc=vdq`E5ZFc>n|KymssnCb zE^`o(Ax^#|^VD}vDh{@q{GcnHfU4gv8PpuyoiK$fYJrU!$-ZGN?L8tbJGC8KykM2# z^MN4U+OC;@xXs~tso`q9{UP+rC$&132Av?IWDgVHS^z5z90EHg7=WV6(eKLx9Fe0O zhE7Zh5{imwq@EYu><*5DKK+=MFc{g0VLAv|da+moNGuv@KqC}CZ*En+nSoSNnpKD^ z?`N3DRaCnYw1{qU{_T6ta)~;6WoEvQ9H@R2Etcj;_LwOuoKLAQ!z%BR+in$%ETHQ$ z$@=R69)u8sYsG!ThL>OahL$)vIT_znoIxxkeh8j1(H*4CpjVZL5))>xxurL?pq%tL zml1M3LyG$&6rEM!d#QERWh3-Mvj2`x>xY`r8>D%d4Y*o5MxIlUwUy?@_J`u}^%3EVEs-fExl0myR zGKwv^FzR<_WvlBK9)z@p{g$QGaynqN{^~Qn_C;J=uf0=XNnr9`H2%Z7q_YB_=KXgn z2CfDd1Fs3}TWrp3N*7B5+ds9=mViR&93qW4P@twPr*X7twF+Og^Whm$togbwWGWI# z1h>gsh{ZA$;50iuQot2_mn?Cn7bV0O+%GFxny>*Qaj{z4GrwN6O-;-acR8oitc^69rtF#=q{D`m3PiR(-@4UnBzr3W4-B z$@q%ryq^ejvLi4jagJTTbX!wqbA4Z*gCnpHH9^b@b0P#w9&GBz#m!Iq%1t#3JfDX1 zM9}0Cx9xBmYx>b9upZEbiHEY7lpPL) z4T~Z;kfiyD|90(9u_e{R;sIg1jGeTIe4K}88xfPo&Ypb8456lIflQkGpoMVg=-%Ua zI^kSMf$>dp_Dis+lc9c!p|awdkIklGjpN7vqxRT>x7xii>!x_3^$bQENme@!b?yfM zV7pkK7J`HOnK_c9j=*Z=^RHzRk7h6A4KdufuE zw;}9674%nZi&#;dn(Q5#c>|iC$u?v*0ALWHBG}yp{sRQ>Hq_b0gBZ%Gf^#q z+gANRb9Vq`BLE`*%@@SG?cnhRdlYuv(*&N;nSn^gpp3MNye({0kw8g3z^K~zwXR38 z<=;7y#djN1L8mG0+RsPIFx_kfMLO4f74kDBsFE6u+8-Ju%1&wFZCrA$_3$36(Qr}z z!ub0xa2@)jzqZ!cq-@*^0M3GbkaGA;BjLCaHOU!`4|t|vhzSB?TxP12YMk0=F&Ur8 zU(&fuN~}iZod-)CA}H&LNN>Fg7cynT&lyv%^sHMuGN84W@HHweQ)B|Qgsy|!zcmFloF#qA+;8FycED8+9|=J~J*OiKoT7&VQSaZq}z zE8dyg^21X}@)Lfe-aK+NhU#Wr50Pjx$haGnwHI?}F)x*Xf5LJ~lpKEe*@iAZhWTjK zIxZ0O#;InZNFD?qN%#3v`tc`Cc#{L70$EZT6-T^CPssq!tkUior}{ZXUeC%8E4Vu~ zYgjGwE)K_6j{cB|pD}-78q;K(QebVzFdgc;Mn?9Fj5Oqqqs~GIWbP%Blx>4bcEyEi`nG6 z3RNA@ZR=rA(e)c6wnEagsKIDeV&`YV_2s^0n$2YWJpPzx#a3S4 z-Al>*FZGQk?^=ZeH!r87GRuCzmu^}Zz`hNFceqcEUtuaiZz<9A)Ra_QKl7=H5K1Cr zn}%C^UU-s(F7EX^%iRG4g#drG-ws_$@xa>Oy~3G2pP7N|QbHbuF8y%Kef7_>7M+&n zBY!~UrrQkiJ?$bPD9HI0F}cn#EWUygj~1IiL?}{3s6@jL!cw19NB=1n4`EUB1Ibq+ zY|ybMn}+g?k@*A~GiKdB~xK7tx3D9Q#kT~0s zlQ7F7138S9H>%~PfMSE=L2x4&42nXhh9n74F&a*IEqV3gG7zc=sLthm62-bJqJUeL zDqBU9jtczD0=J@0WvRQKK!j4b6@31UWZjGFQTbW^%gzRDte%+#xK}O(gQj}*D*suC zAqNBk$B}7>9A|%0TY-zThbF33A+Y+8fY9mB``VFrJ%HTd1k@+7h?$xTtw$q^SL+d# zD!piRcvO`C>Ust7E9Y37NkoF%NG%(kk`&5Y%@#8}Rgt@XY@x(>85;<^a;rb%Z>~*q zqk3-R84e=ap<}Knu!DmGVK$CPv<^)nfW~P{Dq9JVZ7-+(IM{^Aj1- ztZ-wpX?rh}iSF@f78-RuV&^M#aBZ3CMv9lnlu|g=5Ua1bEXw1#Jo>Yl0dD|TiFgP0l?`#2 zJp~j7nLXG4mRq|l&Ew=ZTZO>W#!56f}5xNCjG!e)>s>0!B4u< z^G#?6TRlSbwGh$<0FF7nyy^J`&=O#o@1y1z;+m-`TNxa3M-_tbAc_GfacjD?-F6C0 zpDCXW8s=Pbf2s?LO|4rH(qw;rEWX60I-K-WT+>y{6p;P&=T)zs!h5BHJiGM%F8D+X z=)bb@SBxA-v-up_TJ?iDZREM@O4;-j13^GwklY2d108Y@x@-xVNYr8dN`EAYh>kiw zsrXZ>A2e6GZ1#q`@PV;tAy4pEWDS9dbxXpY4)MLPZsDUP!_Hj#>(QHu{$ri1ykEQw zhG$Hd2CZ1y<>kuVO7!67Rh=##1dcB=OK@cE=p-zR4MjpItjS04wIktr0y)>tl&}X| z9)pyqWzhsG2dP;5=Tt1y(8W!2tRQ)3{ZAp2n6v8x;7NpHaG_@MbLCeU)nO=_%HxrQ z0V8mkWhJDF`y27-rzGRE`zQ{10JzI`M=`#hL`F3I?@ie-G}Hor@58*f9*hV_lxXVR zQtN}bcPJ=?+6m{@Xy%AeEs<*4{9^}bPWm5hkr_ILZ`6>2CHs-~+K51_P$N-JX?UNt zKX3-=&4bJY` zb35S^B8fx0DMt1DA_dg`3<^^nQap?DL(}OBVmr+_&JV}O91_u5{kP0Q=QqVWvGzy zjf~{zSaMwuc@YBV>5J?3Jzn!7i&(nN7+P)uGA1p;e`Y<-M!R^8bt(-MmhQ@{i`=zMPvJUw=@9*ryHNl?(Vj!m zYnr+?(JGRXzL9Htzkyk@dA=UNkn?tADu?oY(^EH{5>ghwL~R4WUL~n|nfV=(!P8Jy z)Eb0k#51343>G?F<76Crr;16GFUG!%nWS$T4Iv(JG>l8Dd@d zKWe~d8(ft{AlD^Cs3lI|7}OwE9~hLZH-opK!q_$;#i4zQ&f{RM)dHrugEaIhu!wyh zU-~)q{nn@^j=W^d=E#OI*%w@RXOom%LyRULc{F|NlbaGC{0R6y_4=xRxmfSdB(ORc zj$HK+jCn8O z9bhya+(j(>j}6jicr zz$y*gl=%^SRs4IUR0^SlycYE6XMC;1iJMGIAUe)9JS(9csQ`)9V9{^-DoV>Y-hD|y zm*VuT>ji@rt6iSeXO=m8f4aLeB6|S(!SJ_-z=%_ls;Z!H_thdv#lQ zS1u<%+PTacM4 zRKk<>DZt-V7i2nkZq|vShs(k5^rtzn zw8-^o#3%gzxe&%_Gvl4624IA14{O!mN8zcwl(7GFB@f&be0>XJ$D;zS2q_5(ASlA1 zh8VWD)Z$ou2p}+@Qr?E1U0d@M$CDn(4P5GdMl@q_M(k8HSxn>yKC$6xD?*1YB3W7u z{e5fI*#2~_~4blR%a!8dCPRVIS40&KXpZzUb=tMRrO&Z@h{c^WHqW=32{eU$(@N>pXr# z?q@Xcolvjc0);p!Tw{AD+Nk*kXXcfa2-F|b2)n@ktw=gFv z3wKe$QO32jbp=@{E^u`zS5C>NqCbXMU;|w5S*`-74Egb6Np&rge9U~@zzxU?iJ(P9 z%LeVvQRthBC~rxNH7-mQZ$>hTwlUIpk@@8kLO ztikeCVE3e2=VRQ&SI<9xgZsfLWwX5^K1l{~uGiyE9s^CDNC=vKU?%!zF+c}P9S9&K zYij%rn_IT`;x|u!p3kg<@qwv46NMOq z5%k?7Gw`@I)56O~);t558jzSIVU-&WTn5P0wj@gXtlpgNxWjO^`5>n&JY{Jt;?*+J zD6Rpb9&(rRoo&xjmLTc4?>!q26{nk!OmDE@m$SDtA5Gkvg~d%4PTZeQ zt%A>in=?a`S2wow9VME~IH+W*BFGidN|i-A)rF~qdeCsoZ3fB|a%E*lCM{Qjr!b>x ztt$$hxDI}surpmlvlt21wWUL#@!9Y8R{b}ym$~a z4=o5qa7s@34&OYv8nO9EyOJGOSgrx@UD=1iq`x&rNo zp`*fqh;m;W52c%g!k)80pacY4(D=M*rY-^z$E<=0V#^yj%|54RD}UR~?rj~OEt^}O zY#W6XawRvyt+l9b8+-tH0)X*SDXi{1o7(f|mEhE|2$<~$9|Bg{ZpFDyzumvB%;%~F zE*g=59RQN>O#&`kWkzHHoKhh|-AUwSfuVrKa5|LBU`t)}R>~7=1c~l|ihT;r?g(F> z(#F648bl~;z=!j-+@Ez%n8QhDwnzChoscX}0rSX_AcAXhMgYS^Y}aY@=Qg9h8(6(} zdmTB}IMj*I;<>N*iyd|76m^*tBXL(vhrApf@03Alm@_zW@->ZKpqto`4&q(0&8~L! z&91X0maQ%$A6OhPf;$3Kucd!KpqIvRP7B|*2z7fBmmk7Pt60o@`uVY)Jb($P@Ge3; znsH{K1uos(NuX$*fkk3MpWl+y{trZHaKQ!yK1_@vskawySg&SxOxTp_k(-LeS0Yws z($AI;(fAVV(a1ekFSgumW_t7|9zEgV<&i2!>VNs_T&p4=sudLX*h&HXgj z;ZjCZvgd>-aH72w;&H*jXbHZNA$z>A0$uWBL;tPG#t$}j z<5qb}jbXRu*BP5=0HE?;?1<@FKd0uCr&aze??JC61qizYjq$0cQ(*d}2|?G;VG{@k zMS6$}!g2$<37KPiF<<>aL%T{669E?*HbInJB}1TY)Ao}5VbI;gNO++-$t{=t+na@I z;~}e}DLS1+;4C|Qja``p>;Aict3E&7*Sxr`GG|&RkI*Zmm;`LT*$F05hnPbeOKmRv z-XM#JpNAte-6^^zGiaPSwv0^+XW2CI@gFiTK07Lh~A{4?YBj=fhin zSZNzY&gK6>a$}oyDhk3-PlGRp$u;0vD<5I9Qu7^Y}H{!VLax1qqR zp`y{U`i~@CZuimO6AA+-H~Y_CM`F3=D511Q0H}SioiIp6ig5YG5q%m61=9%U<#0s$ zlOWnr5>)c7`(k*}Gi=j7kO7pKP>!68rq#Has7u2bDZ-V7r}VN(I=Kz@Zr0!Z;e{aL z-$Mamz~o6G#oY3+i=EJ`P0)IdxWr7vaU@^lRX_5>D>WE7aUdW~0}m@9u$^MiKU%gg zNh|!P+BS&K!FI5n5elc}$*&<&A}U}ipi@f5dtxHGC@AfbTU}e;Z&~Xy`q9c!*7LU! z;qTRd3=`ZupoEi9Eenfont;&c#b!s<5CNUG{YrW8Ip8MmY_c--rHjnLkcjaiX8RIS z24TSQv4f9EA2KrQBPkgpCJ6WvnUsYXiDUBsE$c2E-yT~3V1!1A)`2|>U+j@4MTB<3 zApdxLXR`umsrQ!2mul|ye;$ro%~`yQ2Y}VlDd93(I2_f)tnJwLP}reR*n)go2L}Q{ z;g6)?<9rZ%K}H!lFx@OO;8#wEpF55)7#A>Q=z@i0P-9bz8U-t|;(YB`&2E#l>0d9o zVarlyVm-y7rm3JMj%zEFFwoyKe&b*UTnRqkzdK)9OP&-r@@vJ&j8hl@&dDR#f@acp zwMp~H@A(Hb$9EeGEy1YbA4JrXlULd*xReb}9dX|Ee#GYUURi26=1>owKS)MEkTg(2 zr@lNnlHOL#l`DeY=4=KJN8H)WOk3$CX(aFvc*A`nVSjExbiw>sOxX=Fr zHy0+VtZeJA%bX2Gml7@gQ+U8?y&S{#=ARmgq-HiB-HvLO4ymwH3b!~43GpF@-?4C5 z{x7vqTA}54_aUaGUJ{!aI4RvK`7tqrgFQ+*+C*#shlG?d@v8-6*psJMqTia%rIRkC z0KD#V6SKvur$AsofC=ntGY+WwplH6=-uM>-==Otwv7{XQfQ91^AT>L3NCR;ws2C)1C62xK(8;zirsZaTdULERi@hRHun-FP2rVE|2?jYon0 z43zV=C?VLUmN!d)o2=%o_&aUXjeno6$PKrKJhZvI6L-tiVTv>!DoV3t{Egq?iy#>2 zNc{5zoTCrIFQ}`Wdrd&kNXa$IFhHT1R6&?HS0G`Tj7=?C2rR#Z zw?R(JHX-RJSZgcVocZLZej0eX{hg2^9BP?zmh1qofKD}Fyy)1i@nf11<=L5l=XS~W z!f?>W)=7H~1rxtdYFyNI`W9xEE2Asp06Uf;bJHOfU?kgwk9UDv=#8 zq0Z#iv^Za4RGX3r>NtQaZ*amwK~~85nyth3Ovb)_j6@g(LGfc!VsJoKlgus(QRh<- zWu6~{oFh>rNbg~uLx1y<+tfb8NN2UaYVW`$zpM6~e!uev&koFXzMSmQ-kr=OUj;&q zRv)*mUYM;2OsIKgI5?FHOu2I}nC7^?#;SU=D8ci#Mdzv8FPZYBja~1j`Yc9pID`o2 z7cBGkWb0@G1gc2D!j(72>OMn+gi^uANR>?4l(_;st}+PAB`IPldsL6-m8%u;be&=j zrqin2vUWJn*UuMkgSY}bX)v*xvw7=hY&}uyNz%&C+|cM*XwwO-UR{N>1Z+QT<;X9n z!2MI@V4vf6Yxm!sc-2h(1*KS`3gPov)zi35rUXdv1_hXQ0!xa@0u+oE=Dqc~ z_uo9qCQ6xitfHRBG*th7pkMSW20x-KVhtQw+|mLQiFBZO^de;5ZL1VvK(8rAvL-b> zqgc-S@9|W6+kr3{jBpx$@O%>edF>IbR^jY}bK<<2GXTtgt|(Gje5gz=ye|Fp7CUXH z#U1BYGrJ7_QH~Mb8460j`clnUD|w=c(1armEk_^tR?^r>{>xMpX}uM9L%X+acUy&j zM-DAfJY{76s3dLXMkEiMOcQf!Cdt=Ph=tFwzc{hNEh-j05Nt^EJ^T0F!Dav`|I{dx z;Y0Jdp+VErPPeIG{Z@rjPB}=s{wedwYYKdAP#%EOLV~C|N@?p|_{v(R)R9w8sO^XW zm*>ox2M+865rQ3f>jCJq%wx%nSEPkQ@yhp2PbbmWF7lw`fD@Z90{wxi0Sm=7?uH%8 zHZxye*J`|s-2qOkt=%pc!g5}9>6A6Kyy@3-wXnC(+R9UnhN8#@8ah^L>GQK+(hx$E zwHv-_XXP=9#pD!oyTGpve_N%D~?|? z#r*@S(YWM#e}Kcl7y72R-CwBdZHt+f4Fl(@SdyEE-`Bz5 z16bEVBgp;p@96Rwmxi@*opdC4G;C-Psfv3`m?kmzHzGB5C1loem8z0*&FlD0}1rmzIhg_f{AsxIK?|+KTDUn zA2U*G`hJbofMZr=gS6iudw^OL?3fo4>hI-Lz@Oy+hbcFy4ZJ0zO=piD6-rDE6liyG z^IsYj$#Kk^)(~bpmHPw+XDQ!;Q8Y!G`04AX+Rp>rFM~&ds66ir%)g;0n~YpXH4sE& zvpJ=msBS1Sp=`j3Pu;Nv$_*YSV6TXgZKiQzTY~|K4XKWV-l3o9NcFoar3_reFQyT# zlI8a%I+Evm|0u@K@4N0F6fi#ujgSZ9@#b@2xO4bIyWkyFgRy;;f-3=Q6}4a0`{Cey z04vXmZ&cH!=ybe6Dl(`1L)f8R7BmWa$f=+%>guHFVXWyiQWeNV{aMlj2j^BW5;11U zvfz!nqiOfm(YC@5O{ZPOR7|BL=KAB7t`Ii%8@KC?`r?6C8X*lD&H2+=er9r{ibNL&hO-jopHrksKuK|LYupi+E79qz}n4p>`Px?Vo zz_n9kve0r|5)LKN1V-YnJn??u2{`ZtFX1`VX`8%JNP3L9^5P+Ypy>vh+i~nwsSIt_ zi0>!Z7Uc24k=_tHcO5hv-pg;Y?GNOFn@%Trt~pmbUeOP|E&e*LSoI1k!OAP!v6B-H z8TC--!=q%M)+niStox*leRaILqPJXQ#yrMcy%hMD^4n{D6kObv9>x0Q*4_ypsRAuk z-FmF%M)^9H#K9n>-%5n{hs` z72(*`Z(hc~~q!=1L7l>81UzUDE2$3 zy7a0Anw+>rX($~_OVQJnxMdfN_XdA^Ka$g90vpxLksbzoDI~Lev=AqD{qoS=%WfCr zo!wrutg|N{o=RvrynRw%`Bobwj3EK1?P(I&LqLb8&UkJW{UFnlEM+8{^TR~l^w(O;V9Pt=|3vUO zo*Lo?Ryr1gR9=d)@~K-Ux)sNr0Hw?W{m zV4dKEwr7KCcN4TPS(ET&|byyIAfCIWhP#=?> zq*ljXbSg$79RZCb9sn%381$lFif+I>y?H3(9ha3A%EYWhlPo^eK6vyE%mI~aG!TUh za~f$qNCFoJPSO^~#gZ5(|6JHa8^~h(h+lrH!-Sum0ezUcx3$aFCgPFC;LtIyMN=3T^?=6Hfm zy}q4(_dM-Cv71+YzD|tha^uPk8awwZA52Mxn=XM_7q}{a^QZcizJ~1HYw^zE|A4fTyP`9LPAceDz5ty;5&>Z!rF+M`4^23Qp}qJaF@cd0b1JRN)o@ zHWZ0au)$KneMv;A3DG7dx53aQgQ0V14KF4g#Y!rcX`wC=d27$@*~WRdS^D9*xXcar z_z*~|z)w5%LI!oiOy^2XtCO0>nQ)3Q&FfGpYYecn1YGc|H6*Dv8*cBJ;00d36*R<# zKR)V?r{yHs)UV}dPxUBa6;CpREbMG+U&aPc{Zx4BUuoPT@Rcd#U9M6b6WGu6%Izn_39r|&#;GgfQAxz z5Qx|do$BVS6AVl8-6n=bG=J}wJoryhOuxqGjptf*XenTCF)Iko8nqUR^o4mQ*g;UD zhuXYNMh6JgI?fo#V$Zd)q;n$89SB1ye(0=Fzzi#{(AOFmeZx) z=~-uUYWDpd&zcgfgq+i|)pi;@X5SegG`LO+j~FciX;HqH^8Z5xw3N3F?8je-akYa` z5hqEGLY1YPE#r$3xKgBZ@!=)iEg(++u5Oc@$(2Cy{%B#0Tsxr-pK4(KlRVJR!TTqx zy!KJR!W4O8YP%zc!JKEYY`~IDT{zJBtwq+1TV^X9V71@V&2-;*cie+t@uBvNcJffY``;Fj}Wg6%}J#7N@N<- zCOlm}1?NMe+X!$_Qxb#b!ht)msfsz^NQoF3Cm8OYrFEF!y}UcmC^Guv>~A$z?x&&? z!6RWj@s$x<{G8ia*P#a1|;~>}A*!`{q6gpMMPDK0HVG^1ZYCZ-S3`qO1BUYgeH8*0)=m zZeu2HE)!Rh)xA5Xm*K#i4bU5M79gQ{e{P_>A1ISdM^f8zgK*)%AR*B|Sd&^gWj40S z8uUhaM6=eonBIouDnsk-f#=^LX0Ri{-r#^qB?<3Eo9vWJY=)u}udAQGJqY|Cv;)?^ z$i$Dta1=ARa8*yeQ}U(gk+W_2u;CtHDfj62iP00E$w*T==x~HU1rBji{72AsZ#e4* z{)mYw6Y$1gMw2@qW^y8uw#t{0lZkLY+foy!?T17J?TZ;N7CJ_M#$DF~wcx}RjF z6r7Y6K@~}gy8QfoQaY-WNSWatQrmWRaDZySrGDSxYt?s8;+3BP@4!4ein<~8{O$DW z)fP4AxEt%YAJ2|Ny7l?nC8SNn#>+oSZQ;m2ZEOTq0sVZD?a8ta92#zkq`-3g8(WdY zH8P|f(_>0XoO7Zg)G!-^EZw8qNNsNl0;eF=cT^+i&Gf4A@9PA)+QG0-Y$MMg^Tc$! zw$yO-?V5ZadBvu9dRZy;cy<*VGP@!#w|}P|bqm(40$;NwP~Oxa#@~El{WhrjjnQZ7 z0h)WAe=rKpF@djjTHLjB_>2c_qV#gbVfFoPFN!JW9&jZ(%m|k5^8bvW8^76J`3QFB zWzc^hUoauQS@o$(c>FJ2#8jQ+vJ*&UV7d2|fwVv{ExY z(36?O_jxNt(dx89Zvu{(xVzYnj-PjlG5;~L&#Ybg%OHL~;Z4wG!@K%tB>>RNU$Kyb zA{?t2=tA{~2>N%Q?%X@GxD<(-&Sql78)D~H{x7#sI?zHv?CdV4b<;mR5rcxWM-UFM zez`qi{G9e>iIW*soP!ivUG^~rw052;J;1g%wn45foZvf<3%oDa?gX<%GlL17qXhtFHS8))v66RCDyTW4;HsizA=;@{v_MV|opQ z5}wE&flt&Ig{pDHV)`hF#XnzOCos3bD=8@01$ej?9k`w_`|abrUTJLoFsdULK=Ata0P{OG4mw5`6(4*4<&Yb&0D_oDG3fq;r8mBJA<*V(uo88OK!W}_4{}^hXMF@U#n6e#p0z& zkpXE{q@SBoIuaL5129OWMfKWc&7KOH606H14k&A=BlI!NMF(!Ls}MraPtRTYy_P~C z_g8&cfXQ&Mrg43%4RP68`Z{gYT~*`fo?fB#CrQgXwL2vIaW&>4;|%o(xh?T*8Hkk& zjq#Lw)`XY;4VdvDFfp(|%W0H`{15v&?LQcfi_ntDjG5>|X!p`(NV0utlQYbG|Ftg{A5|OjmH@k*OZ^LbugYeO z&NVVe3?h})@hPRLizpfzdg?|aGffzCV8~@MB#cLqGD!QqS$66mNeAl$em{uIUnv0S zy%Hsgz;~c#z|H2S>5FY2IkBx0RlP`0Dnx#cU9q!F2LPrCa1!?F8IhkRv%rEfh>F-N zHl%B6f_uH1r8WuN{>5@x2%1PbEUHYX!Tui9zp#}c$mrHcRI%f%e5@urWmnp85zIRu z#rXn1`Cwy4;hha|^8LA1=xbJy5tT7gDdA`9KX#y0TCF3i!JT2n_n-bY3sIO39T!!n z1o@gvV`AA(7~$#Sv|wLc%Ni<%lWG%2{gHq;N>P6V(pEjYUF~HqAO6TH3>@kZ7jwmY zM@BRPSG&_|rmp5A{95=qmJj-%2uIJ5WR(vj&Ea2wb2H_bcvOK3%9cIqHWoy{uQND; zTpnUt8!pN157s8Itg%jtp0gfsL8Gi`6NP%%*uw}CKYrDiv?(mwJ0kfH;oWZ@&@Ti$ zEcv+#4moCAdKo{6WIJx~>*}RgWNmjxyXA8|6{xZR+QWMJTEYP}O*5@$RHQD_>hIt2 z2bADmdALY?+8KZtGib;(&5G=M*HMbx>fNfi@IQ>GA41BXzT&y*@9)a_RM-6YpAG1t zzkiR*wbCR;L|D`u`5mPt^A~k()xv)E182g((us+koU7HO{W}X_<|#SOHken&7oKlf zjNO?9T%g_@=^5!nEw(eEHGjgEtu?2?*!F0%IrKXJ(QXf)d-elcpczpxl~R5zOUc0o zX(~n@6bj67mV6s~T&C<$CM%nfDN~HQ)z`S?9dZJg+`P=U-cC?^`ra>t)*$@mSU`p? zZy;tZydf#TRd;HG9lq`~^d_mPPcuScSS*Q$v-r$H5?^!oH1>$30fF&-$|}{;AI@eANb6$=N{k$vvVgO)hO?6<0>-v4e!3 z^$UJ8cpK-x+c$LCg1ZS3f4a;VulV&=PQ&)xF%nXLUsP)X!1uJ{U~ZOeqi7-_ z*}s53b7VwC{v*hh%LVePz>C(Bl?cE2NIlYUc+fx-QG#=#TQxB}@XX_m_?n;hbKy3h z+nrd&Zh)%)ueGmWi0XU(UTSHiOF(w%Zl#ovMi6iTLAs^86j-{u5tOBCfu$QH6zOgx zq!I9|fFiNaU4Q?V@Z2{rXU^1T=FH5UbL6o#tdwCmDyOs!c=2|`{(Ef(jN{yKbzvNh zUKdocLCoB8e=qaQ&=Q5~{sSDqi{Nh_Ol}g@xEoX&TSDb|zYoA0BPbjl**gFN=RH7hug5?8Gz7WC&8e|4!^H8P|LX_Lh2BiX#?n59%IKL;Q$LdGi$aowHXvWZbfkuvRw&TSa1uC#WP z^x5HGaUu~velR4}RgP^&{ynpmxm$MV-3j?`kLO(YvNNd@jlK-Z+HZKe$_W*dXf2TR z&Q)f>>LPpnZSqrQy*c`v-3e!mg&SU|1MBM;zFX*s_U1_@AU@y)H>e;fKiN5LK&L!j zKyRE671-mT%9I%;Zw>w?Dzut8+(*Gs8ccas|3Wt%oJ>8N11avYg#unQfU=?@q;>-h zWJbNJb7$s`K5rELVqjkdpqdH`-hQVk3Ba}wk&$PrxgkH$=wIkFqT7*dJ?OTWYYeg5LuzL*y<)-s`|&=9w4+ah%A z@2{7u5mT*|Ipv?_Ns z4JCLj^6NKfRefGO5m5C8cpehz!Pp}noMbnb0|`_(B4T!YYGlVKxm)F_H}A22x7fl~ zz)cx)<&AWiMIG4J8L*sBhj7ceKf4#@(h`h8{pf}E54&T(1}n}7m>t-sSps7E75Rs} zT3?(lqNhV{^Xm~!&GZRH9xVvvnwI2|mh-1i@pMSBKW!W2Qa6zn7Rr0n@0R?WBaaG& zE#^euH(=RWspjsT{fB)DZ}owxfrZja>hqEg#p-P7vVv0fne)m-^7i1xx@6h+y-ylA zaCVdFs4}G4Y;;|7^j8^Mz*SyJ#0UJ~1(ju#>u1xd>Kqy{_NMpdb|FQny8dsAXP(vMaktU6X1yX>Ov_XXZtUGd4VSZqk z{B_XA^Gw&Ml|mjd@TBCvNHvF)Elpe$sE-j3*^of7kU)zO9T-kVQw%|hYe6O&ZG3== za8m7<2o9hB6OcP<>Zz)Oc&;A76pPZq6+WFNAA|oV+BDSKkAHb^_h0V~o~f9cK)n@i zsQ74cIvZpzTZ+t!K|k*#AnoMQNy~3QTLW@w$di0LrZ(v z>52XzjYOJ7hdKR>d6TwrOH_$2sArXyZYd>XT7r9Fp$`6XnXxBmu8g zd7IXqkhW$uUw3;xoMsOvBr6)sHrAvl8oKS=!5IduDlw9#BNW^$1f zwzX>)@czmHR{kY0>MeS#EdU*|lyUaP)!-AGKZOF8ht!dDs$~*x1+OYDMPWV|)wj12 z7gkTjMBvO+5_C<@4?Y31+$c|~VNkkkvg}bsU zN_HtaZeY@fD!$`jKXy^EBU%TIfG6B`E9vu07U@_UbdGw0F6o!=9O|B8YdC?|Gk(I z>hGUDaBMQKym$B`p!-tK_jfID*X!I#PTjQnt9p`0(S|;J8N+pc)eXp(b;w!U$J(FH zR=vL-YJWJGKhbzR=9>x=iC{XN2{xV7{aI?<4;Q_MRpZ`Fm|ej?AD?3rr6QjTmmG9u zOa$W^4YaUui9Y6&<6pP0x}%HJUht89h5q}#Xqq@z??6GMnM@RiAXZ0V;8SYLmK241 z>efEV!iq<%(D_RA{U*+rKlIs!3`XHT@7%qiLNCDBLtS{N{yl9Wv1s?0)CjQkMtp$= z$1|lo#!~%&RV~UW{OEr|g_CyevjLtN(kssHQ%_7?;ClT{`GUdzr%Lhts^xkh2?M^; ziYrRQ>bLYX^RggE;re`LLK-3v8LqbEHg9x_c7b@!Bx-CcGLERsLg&@af65ZH_4M?M zBvkS_ST?clhW;d*Hr({WEs$ga22Xr!O=MtIPX85)gO|7KI64h@FAMr3kauZ0yMO#7 z1dR^TYm~~%G88{eu&!n;_ci2DgV`CvY+o~_Ng&j2-w0$>HwT6sb4|}@KI_`z3KqGE zelZ(g8mD0w_Ye624RJwin54JAaYq8ErGpB2f$C&Aja#glqD~}3#3fM=n4F%~h=iCm z|J7QpWdVnF?z`#E^LTB+uwIPuqR)PMJM!*KYLQjWJK3lcEBV>H5w!bW{XzA)YxkK} z??GILD@pF>W_heo;bGTN?%p`|up9))LnYMOg)1R01_~k93%4Xu+ZDm?gI9ERc@=(t zcOSV-?6n9~BFj7j+VCc?|1TN2qG)IK+G$#N;i!T;2vPhq zg?^4^{fGst(lHg19je-=)056fGgls%@%FG%`KeOMt}==GNN!oJLlgdP8(ZWamUF6z zF!Rdloh&SU>bc%H*7qjpn39Ta*`Fe+=bGB`%$~JBiKa47!IoD-1s_WQ%yZWUg0E4`O5xHX)g1H z=C5nNAoELO4hD5Evk}c)i|=ZWs1ZsmVo#m;L8dBE>J`!;WW#*pzalCgJP|zgv8gEX5H z5kZN;wj=#yRG6BDqZrm8vk5EQLctnkgw=OF@Nim)I&Xh8y4dfKHs7YUR|q3vH1dVM zx_Wxu)ymEzRgK53u%~WC(yke2j@Q@IPkN{QV1KW%wvC?q_bI>`eBOYpS&ci#2(eorH8$=2^9vD%C ztz`ZvKeqs0D;BPZTVx;pJ!X(BZFu#Fmn6h>6yfpwO5p73!A$^t@F->PIza8}E|AVE zp-NgqZ%Sm5Sl_FD&zQ4Iv4Wx3h5KOc{?B;ufh&J z$2h{_D!_(|X&-ngqKNykbN)aNHK^e7m`fmmlU{v*06zP5o2ZQh;I1e*qjD6rE0=Rt znry}Ij5^MBLYQhMQmFTUBcM&+mBn_JX6**%mbANxP%xJAz3^#hmvBF_Le4sE_>2Oj zX7hDA!7nPDjuZzE>ppjm?dHFry+Uq>O4IYE3e!Fu)fx9^M7@NA+@1M2!5pqQp&S!{ z?YudAWNYrL{e2pUf7hAlk50eqpT63|mB7$EISFvMc)23az@kM;;vdFlORO{vUv(~Z zckKv4?_Xcod(Vn}sbL`KlEpm%HOam4Dw?c~BEo`817dzp-7*NQtxI?vaY@#LO~6EM z#80d%!_#i+p|_FjwA0#42q1l6FAZEh`)eYAkTrXt>pST_luP3CUn%sv=w zsAkd=p)Z(#U8sSF5h_p(^jC*qnwp9UfxKbMCb3}|70Z-aBg?Dy(UV|}B1L^Z0J`1@20M;f7BEn8a;n%Jy(2?6|WWpe$P!%N#r=59dP~a9F^Ss=E+bi zK@KHuA3r$q5M`LgQ{3P?H8ikJWEc?8738ck{baWeO@darlDa)QWph2 z`ijWKOrB>h)|-DEjE16<&Fw$t&?+ zt)$7fCiPWe&scuwaQyv73}%>$bc_L&bo@gUW)K`&GHe2?Z-f~nw4wwtTHq|$p4UD8 z4*Y_%9#Oadxhpbe1Q3J#wrZI%LUo?NM~CH>j}4gq3x_{u+iBwkBF5=```L=PIe9se zUzDl$>yPw6rTd;I=$qb&pm(SGh5TX48}O7?zluM_J1Yf>_fvYij&iJ~&Y73(8c%+l{&_S1SAyq%Q%hfc`~oB@jk9y& zM|ol2j&!yP4+ci)=s({#&btO?2(Kf1J8Q)mRWcu!F_}HitZba8ZiC^cA%QX{bkIQ<60784v9+iJXJRDOw52k;Q_y-g5EczqOCy}&o)!jDy%96_ueI? zHQwwi1qyN{rbOMjY8P zU){eal(WnEtch5sf;7i{v){aiDgmanL%J*ahU@dwSuyKpA!qh~-yWQ(bTp>MSva<8 zNPecxAg$k>V<}lpml4iKuJ(i1VuP1_JX(z@}CZ>5}>b zjAqw~mPi1_MWUjHA)~;!soaf+R<|EIj%F5pBp+{}wKp=2!#{&-4IVJEDTGGjDMo}9 zsm~BlN6B;gl#nvIn%3DLYiPt|P(5M6!M0+(p7U0{@2)N^f~Zn=s&t*KBo z-|AEKOB6*vRKpNyY7{qL-rC@@2(+}qGHZq{w&Dr)*97A0$;TnvTTAmoU~)JHOYZG_ z8uI{UFv(l+k<)`dje;yT$A{7>_U5(9j;oFr_il@~AjFzQJ$D$*riYVs17(3OEy%R2 zHPEwi3!DJRz(W>iRr1WL{f3R77#%c=8aGfz6s$C%|DK^27H>{Er*#SH^fbnbrizcR zWG0ND1>~SU7%`)5wMQZjSEU$8{`1&9VR~5`)@k~^3~4WqPnUVB_PyZc+adHO=lD%@ z=RStYKKe0MZ(1zTQW%uh5<7CTPm%s}TEeF&N6{9rx{;}z%m?)EZb91vV~v*rd{WMh z-30Zap5 z=x!jImUzCQT6$?`{PtZh*wtP)GC_@F#~o%ax8AK7&VRqNwHGyAQMM_9 z4Y%5dVL&qHm?@dfJy65$S88O1Y0xn0&2N;y{If zkcB^c3~N$`;!Wf{-~4V-82S+}t*;(ud%4{Na|z^d(u?^Am_R%sR(t`(pi*}^QZ>01@Pn~khX53jKkE>ca|wIC8^^iRW5ZUQ6(Z-lrwL3)M`!ll)wagj z-@BXc&d|OKM-AK@(#?^xA-$KYB+MT!rp5+b=51rJ3o+J=ydTR*De@mZ4&D^=7k1sQ z!e76eZpYHHg4|IO)2rcn)yHGfB>IG1AFsfe7H#?*1X^>)>f0d2o52JL7c9 z5iA!qg$T)A$eF3*(|(0gj8H)U#4Lu?L>O+hiL+gl#dc{B5yp%FSZvSgf}lLUQ25J% z8+`ba8We0@c_NkX(cLVU{J82%Z)(UKb$O3%_N!kQVBdA+_`U&448r^ShHK!bEUA6ex zQQQJejDN;MdW7Dy3^iV0SVm_t&u7HMRM{0X7!8M~E75=2lwgmDz@|;_l>PYQ)(QRd zLD}~tKRa*RST=l_lRc`nm4wjRz=B5kn+PV$buLf#=Pa^fT=QTKO;G%9$=m>8JI4u$T6TNt~cV&#feaj zi`)lGVTtle+r)nC!`C#K`Ec_!r^5iuqDvyKz>B)K7hX8diuX>#TZcw#irpovxMkR5 zzW#^909G4w&Pzi35klpkT#r8L+4#;jnk>IOBhIW>!=p7+C2Q{mc)jbmn=EyFTQf3I zQ*LOC()c)Bp=zMy%|`w+x}7pkOMiJ~&imRSfiJKcCuURxGEkRb{E1Tp);g zT>~yXVae?y!@g3PI_yiM*^Y}EwnMm3+;TkJeim$%B}dwqI$=zSAg~~L8(dE$RY}42 zKJg1qJv_eMj^lTNJ^CRfacj**3`|K()=67L? zk$p0aJ>yboPefEGi@?Oc&P zc7ox$`$&tfPwpf03-omWW%4?#HbS4Me5p&w zbj3z5jtHv)1GVEHxy9PqDw4BmT24)Y(r}(ywbCos7_w_HnQ|{xV}wctO4J8xJ>pth z`2;%>HC-?|wbz``^nWpPcNMi2nxu(B-fy%>?rj59KdE8GlY3#4fs~lizM8Bs^LL+q3 z?5P)Tu3rAOyp3pHqY6t#pF10mF~2NkqZfzW=eGSE6N`nd7~|hB|DSx2D`5$4b35!@ zxUM^s0y?sz6gn7hkn#A>U^gMR|9lb{XOJE@Q8YouKZ(eX>7P*Hq=$(QkWAWJolO9` zin~!E@3SIn`eG=hz}+*iDm^0IjSW&}Budc2Un-Q~ru>%}qeB`Wb?Q5->oe|Dl8``_ zD(dX*fxgBh{nyjT>PbUb0M$m>IJ$YUrdGdnPzz2HANIJ`?0y^_H&M;6xFXyt%1}1v z_V&k|Qi^&BEDSe9pqibqeq3wWoMc}a-Iz?dZ)b^gm+^NbS)S~3$bUTtKSS8YUG_AX ze$gj+en|~YwZvwGX!1Q!%>T<1ru5;E@k1TbPpVV&9epyC+9wPsD341JtSB;&N z_YmDTk?3yC@RV1P*&i^|atxehkgXluv!g1hf{`-`xJW-SG0L|GBk|AtzPa-fq+ts@fB%651%8%Bj=mU^O8xkM+(d~Jd!`hY)B@zt-{6onrCG&e-+0P`KwyOq#xfko&L9k=#c`#jg;i19KsF(~-owZf zil-dT7b`4(=jRkV#0)mN-R%9jG9iF>PQRPzp1&+&I@mi}OK_5Q ztT>tC8J|4L{hG@%X;Ady;$QG(*SG>vIYjO)Of#~tjgd22E|X6(!r_>UJ66S# zK3Wcqc6%aE^bDHavWJ{f93HSwzIs>HEE< zWb_#VqzdvBJo2a(8at71+WPH9d>Bptf1?5;gxJ@PB9P0n}ECUxWM`X_x6_1^H;Dwn&B?Zx=G#?<@ z-yV)6?)pTLnyS#cf$1!t0K+V4<(|He^2Zu~rOd8|Aq|@!^Y@@=+4P@1idBH{k@eEw z#}A}s#~$2AM49~&jETP&DH@%+`Z`6EDiPRfC`rqTJ%JU$%JdeOk|Iq>a0-#(Qu3Ug zOyx!a>nXd#3^fFIhmM7uUL{$Xgq%_^QAMJ?c%FV?mta6>f<&;r9U)buRO^s*ZbJS9 z%=|jLNBzfu+f{hUJrNG{?fHKfeyXx&t*bY^C;9Kk1(r=U=Z9^{zZo1o6=*drl;0GG z(LmB)z8ZZ$-_^D>m)p$2J?|^r9|rGZBjQYx)PeEqK72M^gis_^S{8X~)&HeIm66xP zbGVW%GA=U7=UY$t&;kKIh!$zVEBQnXbV9<7I{R^|<~B#q~QF033Ay0tgY5 z3`R}Mz{JAA&CACxC?+8#Bd@5Us-dN=Yh-L{ZfRrh= zPYPGA&dp{UO3LwxMyR3&?yM8XUXJ7m{umR#;i1NWmiQLq#W?`xsu!riTmXViuw%%@ z0Mk@^uc;h?@z_*)L1;r@**u@hpg~hf>Ra-hKP51gZK#x z$-gV++f*IwesH<{hLqU6`O(X(o$!Q@d5x|)iJHvA&9a}ZQg%5WwX{0*d7JS1{ccrn zhmbV;RxRYYnJ=Xw72cV0N@!L2z4&tLk>CvvaV)3?zIa>M5fJ5BuG`K9z-z@8*j^+i zCkKrzXLXz^g$y^AC*c3iJ;>@V=gX$401S!Rvia@abB{2hb5OZq2o62nlLk1dAbZ}_KxU(=|lsQs?k za7vY$l~-1g#!2R7b{Va{${{CB3{#WF3y&9X(>?wZS5M*}N{0JYR9JMvhfF$lIFcN{ zeqw*8Ta|Rrb<#38(QI3*i|^-q-WwjG3{W`23=r}J_*{xgzzH1S{z?=4s%@Yx{Xh!B zg7JclS1>XCYs#{ZRy2-M!Rjx zFc^oUkkH)sy=*Od{J8Q+_|-+n4B1*ZFLKBe@h_mqxXwp;({W%8OHhCz(#d@i$(Jsr z@6LAIe8riRxH0wVtu}u9)?_;p12Utk*awYh;M+zQ#43_G`VZqN(NfeEmbNyVgw782 zQqf1A{;Qs+_n-^tKc4;iF7Ad0oB?t|EC7cJ{`_vx9^f_?IJMFY|5;UkA+=Wv9ZSJ; z6@>+=f|z_e;wF2zn+_5E3KATT-<}{U^U<3_DvOGaU<5Z44&M4W~yT+-u z-NDFvZ%7XRx=Hh{^p8w0$d$nIz zr}ZlY2+9Uib}r5W%{1B0bOW9dr@tV&Z& zlmQbXwGM@};2j6i)ucCY|Ih!d^L~E*m&}tcl_UsQN>AjMXW3%gi0vtw5TcoVvKCkH*R_HQvp}{ORkhx1tB~*|jB`e>P|6<66sKmne7b zl&D|rQNfe+YYk=bHR(bL{6bgGt7ZC zu2m!td z{Qlj%cea6b^JHUUrmSxQ(^4Kl#zmk?%pJnD!kN8i)F(YgDp&8;l{L04 zT``yggin~E%>Uxe05K1IHx}csD)I#Hz`=HrhJ+mkKt092zhxqByia+A=cs^KAofTm z_Qmnihl-sc(}h<%+w!u=dSO9A`Q(S2JV_7hQ&I%jG4GR}V180Un#fDlnzJtNvDG*W z`qggpR-GM}EH3aiW+C5|L58b!Hsf|2_+45$onb`;Px;@e+2vJJGr8Hm!!wLLA*SN* zjB~+9kSW42Fx1%4)fiZLjVjlC*DU8Y?Y$`rFBdrHTuV69IiP{%&|}l?*!>TPWCS? zXPazd+wz}Y%9vAi@5yV-N965clia+)2X`K#;$wu>uI^}_60i3uy|-7-;~AoWUlr#` z3Y@qpmk`dG)T0uMeHDMa?NNWa86os8N;YLg!a?l=OD&Vs)2lkAr`rd0RK=v=?|2O@ zC<>AJpo^-r{irS#zKM$woY?@D8rycdEidA7{arM_Pf5=5erKntC6sEUII*sc?GOmPk!w;%+-wAO$eUl}P@$*UBCm+d)RccmP z4!scO$NxFPNc!2*ND;Rpl%&ELyh5-YNYJiGVe`;^-C5z8lr+y^SVI@Tdu9EIvp^>C z4G%>Is2(wgVemu>sh@i*2{=Q9BK5(_)yVd1eNKt7Egv*uewI|1NZ}mq&F;Qh^vlV9 zdv)$zEkR-mj$~nqHpT`ON_FL2jyU&k+eCj=jW!OIa$zeo`BOwaW8p=0zQH&JJlVlC zCMVHnx)MlEbTW00v;RD0FoVa!Z&B=(;+0gIsL1_6yo>d5Q>yC~J6(=^_XNg6mDxg3 zLe*{H(HOMB3xEESom#RZLd1*cwJnJ-fil5m=%ZV#byt-$59Y@OS6suBe#mt#sZ_8JIr9E%~{0o~6k=MzX%6W70hslsK)WrTNa z>7@Mrx;dl{)<=P9`0VQaoaAUn86>G80U!0&{zCJy-%!2aE6d4Hp%5!Z?)O z6x8j-f!KVZ2ihkB^l!N=qoJKTL%z4kzl<+obR@`4kx?F~}c0}{8t;oVQ|LNFD_EDW`zSl59-S`g8 zUgM~DlgTt&!{Y{pw66sLim5q&UQOS!n{MXtj{NJvwoS_Si%9EUwLAv_xZSdQX+n0< zw(tNkIWSo?oCN8J*a-VlsxG&{embC6|L*E(-Anvm0GrQtFj?2>ryCw33XlPZ8NeXt zDD1j#r(7KZ-YwS$J5}|Uk$bKY;t<=J<8%ewE=V6W87$Sj$2Pm2;R*Au!|@SI5RlC& z0Ng6a`y{>lZKA@#r&L(MAuZQIrM!SGNBy;WOy@Z>^@4v2s_bO5ZajuQwr)yd9L%MOE;Ax%^&RH7F%l6o zZmaB}0hTL70Z!$}%EY}E`Vs{1Gbb2sTW#fy^$1|&4D?qg=l{_fE~$%nz`0#xv0KN< zk+1rbr`nQZ*g0UhpgH|`*-={oPRIcBDn0pfuQl&-;_*u3D*ZfH-jjH$Y44HM zhBn@xwe`H>rCX{ysJl!S4|bSk79$^h{`uie#S4r!A9Rf>SFW;0J$Y*Vn}kSL(mO+& z7#|B(YJlZdmrt*udeZss%R3t}!YdCvmyn(w5=MG@3aTSoH$21?Ft?j{LyPfkjr)Li zpkTW~eZWZ-iKFCyMp&w?sjUApAGXv74w{Zht;~hgE z%LE_DRx|oYHoHs23s>mANUJXLgv+I?(=!^$z7wePC)>kUkvCl*;7IMEm4w)j;jb3ClgFn!AMfBgf~WHSX>p~d(&Up!D&Qvst2 z|BJVp9Q3t5G-J+hl<<{`E05g%=`P%M?3=>6HQ?U&b9nSbDjMJP3_S0NRCC;+0-F(Ays?- zv-xHDkud{@zaEIUVo?8>#4UQ|K8Rzub47IxoZCNSF7{XdQ4&%xmO|_N)i#c@^Y7Jo z@4W=bqRf3_1DZ-F5bW94W%6mcUzKriHTxSL(kR#i)^`X7l%w!geS`!dFNCzv2(n() zU!Kd~LRi96@lZ|M)+rPI0~`80a`TW7Vc|Xgwyt+U8zt2ZG~q1&-TyNkByYCX2O$YjLk0B1xD2hwyeWQ42$ZMcNmjp86U9Y2FrQq zCf)hrEiZRS9`$)Qw5gOHp8jms4wkavL=1?31*MuODyCdwkG8=}FKDD1U=aO8X9 zge5Z&qg9`2p>%iw<(D9P_zvB#Qd{a09cMCou=t#$^VTB?8Q^sEB z!YyCpBi}Hu=!;KRw}+{{!~YFtP`%nY9NG>9dVV$z*-*bovCktr$U3fRs4b?`*Q`dK zpslYO+w{LHbMGe5?U!q}V{94>;o!_Citb|Scp|qSIy;9gD{BRi=*E1tIDg1ay=@;Q z%$7tuaP=*XHUt5vCfz%eC2uge;US`c>A+2|>kmrM<;5M)4wPW)zV5YufNJu+_BSO3 z*V#?LX-~+J=^2(yx-Rmm>mN((mC3MI#@Nyb+lRDL|?A;Yk zA|*p z{O;K)9h%8Zpm2{NG)exS2Q($B|x zM!qX@MMO*{%;;D3c%G(Zh@A2VsjTR{f)ur%X$hP1)k!`Lt>toP9;DZ7J< z12?5jv$CtV?^(2~vX{+-LqSlh2{ig?PF}C*++dHI1LGj90K+mvOgDM?^G`(FtwR3g z)_B}^E*G|FW|37glQlYdq8@xb{zF4z6mx9>Z5V9Ax>k`22^jo4wV-%zH;&BZS^*#H9n*?A1Y@u=&+@yr+<^m^jJ7 zUCsQBz}qe{UiCNnKmkxB!i2wB2z$-rN4!G-VJS9<>Q%+O$o6dMVD%quUilcbb^Zuu ze60V`H%4B*7-6;>9{ebn1MA$_9=e6+^$R)TeNHg)p#fjD7ABdRzlEkGaUg+Ny|>m7>@~Ra z{B>Boh3&Cj>Ta?y^Rn6JvbpJFskfPhwsg|o{Zai0sX75ufxjD{VhX+RA)13zKmi)@ zsHac)yP7>)Y}4LsZ04Dt)&OYf6~``(69!Pp035 zwP35mj^5I&c(3ydyA!YI0#YwRJ0Si_9qeb*3qRZat`H{+^^D9od=3KHL*!k|rJD4H zH1*5%;Q#bVgeAdE)(@#sBCg90#5;W8)rSVP1^Yl@j(`#$u0YxS!`D6EQfDvqoOoND z0R;h!-;-rC2}H#gU7XAsCg}|D6Y(obzvtn2%d|lelNkSI9aLZMKOuIAZP9VmVLYe%-`y7z-q;%0+pVIEy z{hHHvV1v<)+)mGq0Qk6B>fLD2_O5>2?RN+Vrj4;+0#&s{5(;C(%U0zermFxQVmf>? zi#Y#k03Y_lZHh+4wcl(*1t-p>MH8Mk`Z$rG$HsxqE8Mf}aw43+hK|J_%M`juwSU6{ z2Eo)>=d8b?aq!nnLP~tb;MhWSQr;R&Q4ZoqS8iTg3<1oG(Jz!fxvAHv43|_0xu3Zl zbanXDLB+2LZS{;KkPiby_2ivprn>kH@+pvDJ?OWYd6S-?q%){;=`Jc8U#N^unyUXT zW08OuFu}yBQWF-8G|MdY_C;KuZ@cLth~$3{T_Y)z&!a@ zwLx|Yf;SFtqX4x}K83sBn2kFRITyY9{bphu`YB5w6YM3hVUf~9fXmnT-KO%Ne*jo8 z&72FdSAl@*xR3#?G>3?7 ztFR8K6T#4^tyrSBShUvnR21h8S0?GeXNM0s^GVisBNBQ&Rx2*H6ti|LGG23xOMN zQpgv;H=qBx#it5(EY<|At#cGm@ckex5lYjztu<_&H}Gwef?c$@E}U|3sUQ-RVe(lJ z+od(O5qbISedWETOh=$`i*$)>otS@3Fe>*iJ9gU0VnxKpkru72l7HSOd{H9(XxNe~ zx~du(=o8R#vFTAzxIuw#i&{9}$>+^I|xn92L>R?>XjL#Nn6%We2{|31d zpsZKLtk8KKV9Oh{*vAPzB zE(G2o1pD8=osgv4pG%Bu`6;y;gyQ{8EP06dEjuzmmOZ0Nm+~eL)M`WbnSZRWYUXpz z#KAt3*~D*yC_rJy#mglE=+mpoj65FR0odRbx`TNACc$awObFbN*qJ2H(|i_ zB|4!YcHhbPmq6Ih?5g#TIL6$g4ItSk!Y79{Rrz7$|Bfqt^2Tt&Y9jMl1Pao*&2Kz} zt(!XMrrSsPKk2a;qy*T1|5P1MW0zRbPw)}Vk9azh2RZ)mpFUBrN31iOgeLt0>eiP| z75ZQlT8Z>pp9V7c7K;>vZ$l0P#o(q~8*M7;P84h@6Y2?CraPB;l%{5@OfORZG+UJt z0YGRT8}F|+IDY+9#X#Tlazd(A@NT0Nem6wI+o^u}U^vKj_vPq%ri`SGSf2?$o35x} z#AmJ!^?_UtFpn1Y)gq}kuI-@~4$Z4_Mi;Sunw7Lw9UGYYKYgN(Y~V)tB%b|TE;=o$X%gU6 zw7QVEIuM&&(@)<{ncbTv!T+aFrW_BUKhQWwTwxkl?5&)8ud(d?)lUVXO!v1X#A+YS zDy9nd?~?AFCy|l%^P*4&R1If~e|pLyU+K>^Bu}l-Y82E?82t$&l%iki$zq7id^{`- z6|aXl&ewxNa#_whWjj1*&vLCe$L#R=v*slN8;xokF2xTDr4p6C^3z#7upKwORr@zt zV8X5{HF^^OeslzZPw!dP6#QU(^W=vRxs5&+7JtJ-j)7rWXZJF$=ZPEjqD}_4Ai2dV zLQ)3E#-u$Z9~KUV{*dSq4tr5*G2}RT#8b1F+vP&?drz_-i#xdi^H_k3aW(u4F0;gd z5RnI*er9vUe=3@_FEcRb_hj*OwBfjKZko5}DF+EAkRT&b(|#jYY0+vqz;?i?mZgY? z=JhuzUj6WSc>E%fmg8i&f7`*(?oto?rJQ}CcTy*uSG9q&W@6~)u<>eAE3dmjVii7H zbI$B0hH{$1zML1#+!`W~@s1Y<3>Sk|adkI51Qak4+UtR*6^dujO9{L~L=a)FMp$MPIhO5E*@tnU>}%YBTYuke;0U7jkV z?`=^)o65XX@n}g*WvC>X!DITnAx+ON<5ls9tMx@e$T#H;6#vB@^U={y%k05^#HlakzZA>Q;Gqf)=y*LI?f*uS#$5x|o;wTbzM|HD1Yk4z$Xb|X(_Q*5`EQJ(gx zFixG5r@!eN)TteJHXU@jFMAoRqr7Rnp$5;%C)a^`AZ2^JB#IW}b@mbl?URBT&>EH3>z|yY-Igw# zC%@*hHCU30({0q6cHcs5o0jm$b3O*icWFP#cvGL);pFO)J#u%SYMe4@tNvd`V2ZR~8yB5jT8_~+(-6%^)e~AWR;GJ@ z-8+SLPOOVXq!MC%H98#WopZ|8_MgO#m*9}ADQfdfu7}2rpq*Fx8@TKWJ?%6{ka~X< zj@Mwp2hqZTLQ|7z;vms!H#}rfusk-@+7g;Wa3h&isqQ|wz*Hx8-F2;*%=3bjMQZ4~ zv$FdsxMew%M8ASA;9uv|ze<}cVXe}dN+&2F_~UVE9rL=+I6H^7>NZ;|(_5E*JAH|Y zP>ptB>2QqJ5a6T?Cp_@p)mlfFhA$Eb*`QLtn=dQFa> zvUd&-Mee9^($E@H%Cq0m%hcg_?jAJT%rD?&rwQA!BwT7}?|;~K7!5wY>N5>_t2yDt z1?6F2+E1o=C}4F_$oKk&hX@37r!}??Mav1R)qQo^kplzfNK)Ew$jUT620IpPnjcNs z>TQ~DdfP_E)y}@&lmqTZlkYON(N8S*YnYOCDjs84cHYe=rL!8hL0Y&aJWCBGH$<%7 z7$sV)ja0q?K`8>yt34OLy}*6;l~)=gr;svNS97AR(Hr~fIHu4Iio5^dJnmB^^y${X z=bZcK*6#-|U+b=E-G7D`T*CQtcH?cpe3+K`z#_k>0L78I2EJX`v(onYJDLZ77e9be zOn}I8Q;x+Kh%ugVJ?1~2I9NC>N{_^wQaI+%DmNJ%?21cDW%)j9w1i>Phu6ayb;4!+ayxR!ynztNiy&U;CskVxRg<~2K8d()gbx(NF z^k>#X^R7fTP3m*bX2+nO+TEp3D--`-)caNuNdN$CE(9Heu#n1dbnegV6Lkn|5h*Go zzr~-hav7RiIu6P7+y3ncQ%b1fhOiUTgC@N_1dHmfcNnp*#RZNm@ z#frX$yz@Vv>-{;VxA^?tEr^P`RYE8cL6zw(QsG0OISro6BE?bEdP~(O{71sim-DbY zfD6kPH_UlS-L;LpzMk|Ca&Y0Vfo@6axI&P>{5Xu;A2mhebJB1_C0@vIEc>&E-Jb|U z(?&l1W0!2PPnpe+Pnks!+@c}K(BLMe`=|FcQ)a`r%HLEZVeFl!#%t-Mvv*H#q3{Z4 z)p?fkyFY~ws+v5ph*Z|s)Ab~hqaHb03a@y(BHem=RX)N4)ut=~K&fB2G%}Z*y{JWk zlXsMFct}850o2Ccgf1SGT87^0 z>CJz1IylNxfu~EHLHFnmj8mUPA-DR;OKWE%u?)4)dmd86KJL0IDc{!R!rm@E+Mau# zuMH6TVk|+`d|R6IZymFXRy2}Vn{z1^6+gxe>B)nj<=c>kut91&9nDwc0`XK(4PFcZ z4dR+dgCGiKLOru4heGi?hy8Z^9!$_+pp#Jbm7|h)*Ix<20qv4V2&J>>(Jc2cfF=!P zX~v2e zx0F%Kdhy;im9`xXs~NN$PpmZ0t1M-iCu$_dz%FgS=~)7#rIxNX>^ns&I||--nZM z=m8>;jHpoqpPFAmK>i>1XV=ajPv#K1Rz^dT=dt5ax`pJM|MKt~|C%-$3Tp0ON)q8n zSjH97vmblHoDNM?)bC}@C8{en%x?)u9kqUP}kJ6S~zxEsXtBGS3r zv3_(mrP;kJ9*N2$l!=xHBr#n=#^23Ohl{ET0gx(7D5*WVEDo1M zg2^J{RuUNS=RqY&J)u_mR)Z7cnd#K zxG!y{q+gx8o14=C1g89q?uJSx#eJHnm|j8LKrM?njg^L*!-oHseZkaL$1C3&hOB!1 z7r9-oBO}HH?5^+<4gKDP^*d*oiRItp;Xh1DvI^?24lY$X`&|_o(xMre zO2=VU5AzSSkZ-!4RdZDjDF3;2?jcvgTIF~@N%jbIBZy;4Eoz&l(wGEjcH~g09og5d6Pfo|LFsQxiP(+AW=jLaMjhI z@0t-@nE$6wdQvUVb^WzN9jV@WR7J|-ln|Ge$)gG6Zak817`eYaxK>#ur zDM~}LkD9J^K2EHIyhaVTF4Dv~9%Lk(ZvULc#-J360y=a}>l@Ns6p?z|nH%X$9>k0p zq@~)ei2Y8g(iAVLDzd>G#(0moS(BRghQl;bK(MjI>u;n>KbKnu`|TplOq#TCBG0LR zvLq@UP_(kW6o|`(YNxG%$%@EJlC3ylV!U71GTQ$0PZTVX=Iz>&6-syzJ>~Y)3cO{i zC?wo~EKS<&xn9rZI8#kHTBwKW9)lj)wh*PZQTve{h=mvq&-#5Ei%0qZ4FJf!XMjVt zbj>MtO4~G3sgNYQ`H30rPR4LC`dnQRgE(f|?&ytd_BY3bSMyLgLbkaiD<&q@9ACJ6 z$bhxY^qe7wwOl1nV;Soc=4ALp<-Vr9PmKI5u#pN$jnap~WUO~Ss*q6TscvyV`lQ*s z^800#6snFsYA&gG_bv-8MY$qL+lUo)*#GknI0j}x^A;IqWd+|r_ozVO;8?T-sarje zmR!qo!BW7-pC`5P-1Q`GI@2oQg#4n;mvLz=!+^mbWL}`?R$oKMppi)l(7eH$tin>O z{*e7@^~l%@cW;X39^O}bn*fla7!GunDy=){PhM`>X`y8A~j*RJhBledlTv5J@!X6`9au7+`5;7?TQ8z{t0L zK3ynKHD?0Ea^|v#IUn;lS&NPrku|2%#kj*QDtop~MB19_rr*;q?WOzQW3n8B1`HQ%f!rOp{L;(Jk4c5|47kU?G`|P3Qy`%mI9!mAR49QpxHNlP)|1&S3E{BKY{NZ><;-snnh58qa{O<;bKM{0GfhsAQ4b2d)+xQo*HzxqLdshGm1OQM7lV)y9Hw=pX7{;T>rFgNmym}fAc0!-Zs=JY0_l8=t-g)tkvKHZ?~i89 zj2ekz*8F%SYL_e3TAt^Yt93Ph)7d<~rJ47Qd6 zx@rB7dO@&vczNZzzr3R9CU`|?>9>i@XimrD626*qyN@aNWgC7IbYrUe{kt`q(~nt6 z%P)7sS8ahKif2?Th0@wW#=*LU&mfsmi-P_aTeE<~={eKrqGKcm;$EvcK2~JL0C2p? zClVK7i5wui@eg4X%$I3O59-Yzq|O-TWPk=Ino5%Px;vJp@rlQFq_W$UKZh88j9ZUR zh*}7se(qlXdVg#E&LaG|#Euc0$=Clshp*erClo ziK`EW<5>%2L~Pd8Cuy3Q=puYph~Hx{v}9(j?2aZLSQ6nlLdl$-MnA<5M7OJk-a8#1 z^HP~ETRpW!_{BeKsP=zX!CCMB${?wA#@s0+47IIkw6D&y?4OVj^7f(KWqB-3q9ld@ zIP))hY<|N7iGo?vOl=A&6bPu*JtPz+20NNcdTgyb79`j9#?!1;P>sIl%%i2EnslUP z4|R(vF6h=fPi` zHP>9(yL*ENXCzeb($Q%DK{0n$y(cv%8VXxLhJA3?>5a8uYI#ZXxWe(G-B3I}0KfI# z4^nsc=^9tO5Qpj%JmkKZMUdv)kVJC_W@skA**L;o$`-W3+7wq4Yu=7o13+vpZ;1P3uOt1XFO2w{%0#zGG$bmyX=_$qcF$@x?PX%K4k|< zLaCN;Ju}Ym>&>3ZR>>8y>bMYu6#!_#pC<8)&3ih7A6jq>hka4u?H{U)Rd`&!^beXM zKK;3u_+;%h`I2s?lK!1t78$5B1!Bq;W1(8k%miA@Sc-)S1PtT;JRR)gAdZE?To6>osrF$DjqnYa%c7m)?aN;eOnwD&KI zO2zKHH?F-c&8ae_aDA%={)UB;^&)t1Kvx#b53^${NH*iW*fr!H#@W*JU_(F|ezoGF zdC`ibN_@O3&6!T)r_jKJ!4$(5FaGFM%i+VvgNf~_{Pn;u?3eENX@v~G%8;bdxF9;K z@4x`vuMv!@`}3LuVxAY>bn_hsgBg@0{tJ=rMdg?~p4A)xNM>Uorsjp-|9B#qCJ*?$ zyAc~otX&Ta5EP+)te_$^>nxScd?K@o;v~b0n3~$oCC2*`YR&Cfw zWL;Vb>94&54j!?$Sm3lee6!{e_Ht#fBJob<_1z_qP?9yfn*TMA1|dGoo=N|J&|6N3 zTj!yZhY7)!flMlE8b=**jdVV%cIP+|%Cn-LnC`93<<9NF!Wd~gQi(h&ueg+*zNT;@ zP#Tu@y848_p7HWVTCBMfb)fl4V|pU;2BTbKm)66}62<#(15_u%SKPxAwr0MRLbHCK zemUwSFo|T3KUwtd{HU*OiTHgzzo3e05U4bb^Qj<^bi+qx>+w2%&0DsC4v?zuu_*N% zN#%Rv)Lmnr8-PE+lhXRl;_H|NzTmE^s1X3jsTRqHC$;!ryj{-|W0(xQDNL^OTi9n6 zGIem3i*$psZhs~5mlGcvDHq2-+-Nq!pkxkZ*3km;A(UE}R(ZkjmZ~4{er0dsl1dDE zBog-o03a~R{tH&u{>e6NW2OY1%-sgLdKU8x!z+yrwK{j?$4W~no$V^Y*q^rHaW=Db ziQ@~TYik4g&z{dc(Ezs?JSvlN((1;Yvi86C%j%e@O})7CH^jn!qcmR{I~7!_k>Gre z_q=SUw|*~MfPP@QMqF+?uC(esN`6fyoQm%cfZi9~x{0^zdU}uHh9{HnwRNLwg)`X7 zNV0NW)iEsz-kXp+1IAzN8K%4J7@3!p>=HR^GiEU%?s2!N`a`Xoddbb z8wvp8i9?vNs_a8X>5dW|e=E~d#%dPfmUxmMGPtF(zX7!G%nREA497zb5y5~?BuJ_v zS*h_fs5+aMo`4t+l6X$-B9r*Vaq~|6P9DLCXa6MN$nhojcVa2|$dJ0j0XGi`g%fw< zk&;V<=8rMIVXuue*F?}Yh~bzw>~6*jf{K(Ip6h&l;~xt7e*R7CGDkjFa64MOfxi)` zBmQ!sB*er11(4_Jr`S$mi{|8wDGQJfCy{t7Ak<&xm#Zi|v$4Ak(FObg;8sQ|rL%yS zimDb2O72{x*{0N}oHLNULZgHYQU8~gT~pBD#zBh5*vKji1!J|{<#sYDi=QPun|(Kg z(XPTvlL`qvW0a1`C9YNeNv8Fc67}s{;sRdcq^J68f9mi`b|FWa4@qRjxun}`WtV~8 zTfxs9M_L~?zrR!8J*SWF34r1(`~N#XdcA-8@S2Cr6o7M~OPmZWHOSPUNtQumF0Jqd!mV`80 zTQE9~^9Z(A@pPQPo3f}gQam_5>U(xZS)V`_hI+ierWUuzE;u>aaQ`dceBtZ0hN>aB z*-yM*ibf;OzRU>+LYHkb)|9%GYqW4jERoGguKyvSajTOh+7iM47|YqhDDSEm0x2W3;j+qIOc2krLh5 z9!AQ7@CCom!VaG(Np$oH6h*B0P;UjNpMdej@x>u;8bmvY#a=QEudmYCr=2dfrf1`6 z=L100JaBsvuo%1Hx$Z|FG3l?#d0O#v>!dLg5`nkO#T%3x`is-x!EvR?!eq&m{SWFqrbj2%DIoFKm78Qe>YFz5pDDd{U&B1 zarszIIJ6|6P?~u^S74q4FAEB?zlUq43r2DxCKZsGNTH7;HMXu<-a^iAURge}{5zg{ z(r@fJaG+cdZ{wrZ1|y$2pHry#h>1iysve+CC#|wV3zS{#nwpcIm`$q9`!$O;#Y0oU z*Z#4Yr-S`Zp8#qD^l!N}{y}spA0YyaG?H#m+Q;N2^8Qe2M}R#9rY#j}lm5Wlg$Q4q z%#}Uu_>hXwX4_gr8lg3-46if{kxz{DA_o&bt@9SdK%l8<9;F@T$(4E zuwiS~i3nHHu|UR-L84p~lPk#)jF>8q$JZy#*l0k{T>`U?*S;S~qO21$DCq}Y;&`cf z>bO`4dImF7WdmsW&p3?c18eKe=vM9Up3kp1E3N12?pU7FHq(%#;v)f2;8t#{5tAg* z4bOG`38$U%rck8h$Gh$iHq602mg3lTRb)DG%~5=*zFdj169fJkS9-Q#5B{pTYMo}c zm7<-{TvQLfgYZiD$+s*yU=09F0%sfUOkIlm52UVMZB_2|Jm7>w4SHLt2j*xKtSl z&FnnGL$gp^GWrz7M3z|kB=olR2LnrzeRmRjBO7-rsCGO630+}(Su&xI=JWpVybCct zEQVI!1?r6xR152M^6&@yn@dD(t703HYJ&3{V-Yj#JaRO5pE?+K$y+hEOm+~9kklMq zxLMG?cpKVScuG^o@dE(lEl7o$3LK?0hVI2{JG}6snjZd9Y@A)9=il;dQ=Wdpi8i7{ zjFQCZ_7KPIb(QgF{r5&knMh46Na;jlZa;DEH^(U$6{~V?97$llRCnl^o0089u`aO< z3Lf&97&IGQq12n~R|r;#Di5M9m=HSNqLg-WK^vg6@L33>XSEG=r(1?EKtEOUJ}J>xt;F#g9_Woe2Hv73 zIKhg#QrA6Cu!ey|YQtA#C2>u+4~saf+JMP4f!XTNT@cS3*(8D$6ftp?svpeSVqVUA z^E?F-$t>-E_^4E^)M$BP*72$_-zo23=Yik6ajvZ=@&n~ug8WfjpYU^RHDbm3Q!|Rt z)V_CgvpWOB{S1JwNVVv%_tidlOn!u6#Q-5UUCL*_O`oxtH9q>!KT$9rYJEK`<8Fkr ze5nVC6gbyFywa^4nN0)_D-F1avk1%sXw6&|8>Z-_> zMz$_5$rnJcW!Y0|+c->6;Ok5viR{#pbL1!W{Bc#zuY1Eof)5L%)jyE)?B~NnfA@gW zY3iFxq`F<-558V6l(0D3?Oyadm6IEQFdJNMITNyP4q;v7x0Sc*4y%dp6riq`+MK86 z-02JP=KY>Df97EzmVi`d6{3Jg6(tU}0)Gz5F{Tfj-jaVz&O@5D5M+L=Jl6nU@zp77EF7T5n%$#;b{ z)kWLxgai_L2)(yJ=p6!rLg-aG2&fe4ML>#ROQ@muYCu3rfQTSf0S&zfDhjBG7!(iz z5tLuC-;MX&xASuInD5I@M&?{&%{k^+E6jnwV2T0)bKAa(6|{Dqm&j#(#Mh0fQv_i<*{+muDu#INy(~?qSr+72L?3CJm&nGezQ~)r*6laqT42 zLuSa=)+)^lkefvYy!wXh`9j6rrI02PVesO02XgYKAM=HelRRu0Kv42vnT3uYD zR&!?6{EH6}6@v!Xj|Wx(YFXtE_NOVfYT5n)bTSy9q~;k!?IvsMjJ6$_|sUM^lpD*SR?CHeCl!uD>3CDz-p z*wM!|_yeQSdXTS?tfzx{!JfoR$CF-5RgT-lJk|f09%Ec38Y0E8yLMGX{a=dkjM8k# zjO7ojQQviz*LQ2`8Wzs6Y7Y5w!O zfcZu`ijVH3LQ-&!_s#{=7&p!ARb%fuWJ5O@drq6ZFie@h%_hYB?)vi=l&<|Yz6rxd zh(E=%@-`>N4b(y))(-uZL5nYpTpX!>)FM$P+fI*)e4j(|gD1xzTfs4(qy_KW>`Cgn zlqWaFkTZS!5SLT)6QAad|~iXG+>D_cVff&0B3sWV#Xbo zqY!Z`v$_o)NUp5>Ws!iGPk~TEu`|Qy}U)!tX4wh49B{e47n$h72VZnK#1cgmiI^bak}wYI+E&*C6|>VApt?_Y5myJqSPxR38YkWxhI1iPO^S#iof{`35-&UD*% z&#fZC*Tu&9UlE;4F#~G~xx=;&FRoU$5Lo6@31Hq=LbNS2#gFiS^;;s^W}8E=2At2e z`Meha`AvhWrATiA^DUjcMn=uuTDGTM3Tom_D^C+(Po#@oB-u6}Hf$;n#iVNZFTY^p zsXb{3=xQ*sV~E_!1r?^68lvNJB72L0CFRHg^U7BH>>P`CR*^tIT9xhD1X2jfcM%0u zbCTE-{IbZ&BOduwy#SlMc=Cf^`*W|#od9E7?e7y(*A!?DHhO`v>i^Z;uVp%HOYK^@ zGOtCt+~#X~kJmkv%j}f#d9K&b#$hZSI)(t`0QH6SpPvuZ6VpL{Q*q+Q)CJ#Xf^xv^ zoiqrmKHepyCWCa+T2DIubTu<&Md_fL@lj)VBLfqyiYaj>mmlQMfMUf8>T%^5^SHV$y!2a|q7$1RCee!j8Z+Rd2tHAgS?06St zG!E!v6P$8K@Ytm`^L+Q&!&%*x5NoH`edfl8IMW(jSamEVD(=Dfyj*^l#qogQyj zgxH>$JlbV!y>IGTdJie5Y2*8_3g0HtT`UL17&Z_~6C;S4gldQ<|MjPy&)1I6@aM01 ze|31dN%r{8cgc!lM&h^fDbKcBgySO((&f1{>m^IDM>zc` z&3fR^Z6aH)W`nU6|G-gvSRtrN?wM^Bk8*{#!QnzIER3u!a@nhm^jc(tDw$gKi_z_e z4d;uC6VsE=^GYDZY6T`V=AlRKZ1Yk`BNKNE*!i%^ippH?8n+fX*Ii;y50@Uh$!JgL<9X%02y#&9hu~IH66JSr)Ept3t)th?gK6HD`qDYvQTi4e<1I zLsySui24=JM2$vXT^_2e-FbHshRSiKsXO-;?d7bX$Yn6?PMGAnD-LZtsz;yqe42Jr zbyCj-Qd^t3*3IRql*QU7oiTw@MPz@(hYc3fNa9u_yM?C~OkYs5Se8A;);Be$>9pPq zm4KV15x4V;41lgyyRteLgwj~rv`Lu1XPcE?h$l8vwnh6xWFSay zV~K^SZ}Sq50x1tGM!bIDp>VL8e%A94fCy%Xqc(UK-$!_OJ(XT5OEWubdhTOQ!KO-w zToB|r^WpaxC#gb7I*O4+PFJ9kCUM~=Yvd=DJZXlTTQ(PPi3fZY$_K5HNo!28)8ESm zBiM6TcY?7{`=xoSMfcUR1>TLKcI@39u9k7i853{Wrm&DRwY$lBJU6p9o<0W?Pm~xy zY_j`z9`OJ2btuQFKG~#%2R>U)a2OaLkqCMosWBlj3QWz@SSeSXonxRay{6F5I#IWZ!*DqQIcErhq7L0|asRoZ;x&f^1^ooS zl-U5&R8zjBu?PE-+JcZ0Ilk?%mrW;?ZRqh{0$sa;Y^>uUU*Z+C2k7jH1#oR&fZ1kq3Db$IcY)m=+!TYR&)1{@NhuX&hb19 z<ZsMyQxvdcf_-iV0=VusB;lg1sNH$P$m5D9QQ>8Q(%C0IVis~t>9pC zVN7Pu!h@qO4jXop(pmNC22BjUdKPMu`P@x4ho%+4?w#_ z5x7kK6R-fliLbAeBXa4dTH(H(5yb#0U0hEw#~qW^>#sRg)3uK9a6?ed+|xgC7SmGr z;M+cdI`Bg$jUAD2@yv{k->DhGm8yTVv*_m!@*dp2t~qJhE;NFtgOsMVvZ}@Ol;FdF zlJidZlb3I_5XE#k$0Ba5T{DvT$=tO%`<)3$x;7I2q|&Cx-d@u0O#=)2S+i)rEVpvS z8i%^baly!%^?`tSOG})h^?f>vVvqC%#6m%PiTzpxG%)D4sZrd!^XA_xv(lR*Q>4uO z)4^oG^~nr*z)!2g_yrG`ow2Zj!+raOw&nX4At$A~q4(DQ;Q{BTJk~!U#^qAJAfC6N z{s!(paK)M`(mIGITZ>*8bR6$sSv#)`06=If1j7kob*6QW(p@_I_+_Ht3KV5;ax{OuVpYzJxTC6iz90l&h<_jWW8Iv(({m#so9LR zH;aD8rf0EF%wLoKb@!vX*9GDDx~781^T+E-tMmAyztxUV@-LQ9;FWkgCu!<;-T40rP$Oz&SO} zUhykm$hQ+dKkGi8Ay-j(dE=D9uALLaV7`G*1_-?bB*;?i1$6_ zfGf!&DLdc5XId&p)=F7eM!Sx5nCNC%{Yt&Ef-_#e7jhyoFROG4wi}t?79W_^2d|@O znguXLKDb<3JO2bR6GUsOFzKC+>z!MMW{ub58lUi(%)=_``(fv4>D84(Sqv6ts?rlT zm?Sp!@*k%3N_>FYHC-O9F)HD+g?tniG2*s$8M!uUF=X zRFxsw^Ut&Vr^DG>R666juh8|+ugnY1Pq}A84LwQN;ov;KKmoY7t;SA?l6VDj`?>rthU^^E8QF?RjP@yHA$+avnund^?3LW(q&0^W`sn>GJ)s6-iM887<%kc2A*deinV(3{OtB+8BIWeHzp6m|0*H69!ug%o!>csrQNR#bAOJ?PbUXmGscFdchm!`X)Y_^_8pZzVA2cq$#CFM4k<&K#)ByCPyJ1T@(Vso2Btl$jWdE+UYhQ!* zL4Ne?>0ff{C^KNDzR1Kku3B}#QN|fn0Gzm8cQV!c_(6lcLR`Wr!X*#uWjlUJ^s_JR zcMIi{q>Bp5006?>Y^76T;AQ5f9m`>8L#>e;9RALuWG~7cZ`^|JqK&gZ&wZm2c3A9r zb6D5#2#+9me+YUStPk{Ll7el00-a0`LB2Nd^G-vq$EK6%0DvjHORzuHfP4G;#ZzlE zM?0(XoUqcj@XknQHAB&#KBex*S{-RGs`(MT`=epR`H6BZ;A^F71g6nodd*#Elt<~m zb(MLgeE^sAAy@Qb<8|P1JQ)w}XZD24|PFcH@F% zaRAaMQCERo^z<`Et}817cka=89a(n5A8C>h9jaSJZp`|%VS)z{PYWL+RplA_M}_~@ zqryZ~m(Wxw6M9-QDd^|9=6FW$lPXjtq7;>f-h`?G)PjjzhLEubU3g&(^r57?*9oI0Tnt8YZ~=OudmH{~={BkS=@lX=P7 z5P;x*8&Bw|$G(6_2*}s><#+b+N`DhHNMnQZl#A&#UFC~wMDJOBACx-NnTK#Lrf7E>kQ5D!`c}>8=RN8~^o6 zRrr0Dz-ildc7NE+H7{6+U$h}JzmW`VF<6qH`Z7e=Xugw{I`rBBhfgK^l}6q;e^HyN%iA7AqeI>O9i|0 zP*m)Hed^O1{bK~Uxvi>^qfT)hZ=U>bBdAty+pyXNZDm6Jd-+z1@IouNh_u*8%w5VX z6gk7Oo|(n9fy&~QQFE+~4!cjg;vc>o{S0iJDf=Cv%(yv+KK#cA5L77ICR898Cs`N# zKww)7o^n!yN^KQS%hYPuz zW%I54sRNfj2|4Wu`;JWZ50m{K@6w8YKBNH9FbYyS9{%KETptq$JvK7P+V(mTqn0yL)p0`+9d*&{p>5L6A%!)>I~J@F*n$0F;x zjNG<5I~Y6fV0hqVfBl&NCNZM=b-&j)QG!~`rubE+>;wd(Ohy?wN<2m(3#;bGEMMoK zVEsAx>>Uiww%gvdBrmxvlOy3c&z&akeLUZvp>u}fcU$M-H87ppQfWPglz}|*=h;O` z`PIk%3xp&|v$()G01%tlnoNMVlMn_bp3AW3iHiB^b|a+n4;jf{OTu`*nqJLE zCrgeTo8AQgNXzP{qoIr1jzA9f zD(H713zNwm8%YsrSr&$zo%b|BGas0=sR+@Sr z*9pL1o6jx8F)R%y*E(~ohmHtDcB$hl!nrb%sv;&+byl6?K7IKf=5jhe((z)DCs-Fs zvf{}$Ll3zn6fxZLuA1AiD0x&w>Zif&jAOpMBqUwbcd5^3apkK#vDEIIL=%`?T({2U zN^!F3FOdlK>QqBsGivYr@8>-K&NqU1azTBE;aIu2k=z?u4k`GWt=bNCCny3bb3jH^ z5!D0Z{q{K1V&#|!s1PKJzD)yC99uCR1AA3NRbF0tqHJ0cC9!!Hm@xlzHez-6TFxNg z{{*xFTB2B5uoVMa59%m?PtME>;R)VE*8ra$P+qcff(tw%NE`8l{7xt&P3FDdE=~#ldTXjh=zQfcemz_YAjhkPI+IZQb+Ill1+ie&^VWkt>TY z!@0_u_a%xfQ7Mk0|NK>Ipnnw1Hx|8eaS6G{LW~0c`cu|6@05&h^jf$1q!4 zWK@(}+7)oXDl+c6CnCuOvr?8YFYGWP-H0fT>H1;1FlKGCa*4m^OEmM!co+m8j5>Eh zk~SK$xAm6*+O^uyV>Mh3wBGB?OK(uS__tgF_x|Woba=+>>GY8Hoxi(+hc3H^2IDZn zdS}1aiYEW`UK~WN3GBBS^|8`mzNs(?Fw#zfXCnyWwHb|x?cgap&B(2_@ zOna9vgY*^(DnTlXbA`u)`2s+JG^hZd0zWTy;hFOT3lr&aoERpW!4k{XV}#SpBibJt z^1nJU^P+mZ&`weY?!>}5z5D3sgRrEs4sR=oKq7bP<#N14^`2UG&Bx0ZaXyAngW;*RnRWmfnXYhGmH-Tp8c(R#0N-eFF!qDNSHByx9?7_=REW6- z0jt;y(a%{@rTu z`_KA#R9N8O-%=(6bARVy(!76Q#jXHmSFpStsyPs|6P->tkv?ry#D^=JjcPY2LQoL|L%`x6Q_fXEdZ$ppDYdl Qngs9<`CmTv|NYkg0W#=DO8@`> literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/6-bass/36.mp3 b/src/extensions/scratch3_music/assets/instruments/6-bass/36.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..d967d830ef56e0a9450a3032a47e1d9970f2f0e1 GIT binary patch literal 19738 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 R!vQ!AZqfkUDz%FG005K4dZYjV literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/6-bass/48.mp3 b/src/extensions/scratch3_music/assets/instruments/6-bass/48.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..5c8ce5664ef7da7d3eb1c002d4496edb1e6c5df5 GIT binary patch literal 23175 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 ORRTCe2Y&y5{rMjW1AM^% literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/7-pizzicato/60.mp3 b/src/extensions/scratch3_music/assets/instruments/7-pizzicato/60.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..589c65786785ba934243cb6d3008597ac8f88fa6 GIT binary patch literal 15102 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�|fEK0Kh| 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 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/8-cello/36.mp3 b/src/extensions/scratch3_music/assets/instruments/8-cello/36.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..c337dedf2cda849ed2ab020ed1d17db09789411b GIT binary patch literal 25512 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 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/8-cello/48.mp3 b/src/extensions/scratch3_music/assets/instruments/8-cello/48.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..c4b556da2f46e54069966a7737bb23b9c9c1a664 GIT binary patch literal 36240 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 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/9-trombone/36.mp3 b/src/extensions/scratch3_music/assets/instruments/9-trombone/36.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..95c289ba3e421e368ec8ec03551b282771eb52c5 GIT binary patch literal 28495 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 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/assets/instruments/9-trombone/60.mp3 b/src/extensions/scratch3_music/assets/instruments/9-trombone/60.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..4f00cf119d12e454bf113d17d30f8a6e354eea1c GIT binary patch literal 27147 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 literal 0 HcmV?d00001 diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index fc07b647..9a200796 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -26,11 +26,11 @@ class Scratch3MusicBlocks { this.tempo = 60; /** - * The number of drum sounds currently being played simultaneously. + * The number of drum and instrument sounds currently being played simultaneously. * @type {number} * @private */ - this._drumConcurrencyCounter = 0; + this._concurrencyCounter = 0; /** * An array of audio buffers, one for each drum sound. @@ -39,20 +39,35 @@ class Scratch3MusicBlocks { */ this._drumBuffers = []; - this._loadAllDrumSounds(); + /** + * An array of arrays of audio buffers. Each instrument has one or more audio buffers. + * @type {Array[]} + * @private + */ + this._instrumentBufferArrays = []; + + this._loadAllSounds(); } /** - * Download and decode the full set of drum sounds, and store the audio buffers - * in the drum buffers array. - * @TODO: Also load the instrument sounds here (rename this fn) + * Download and decode the full set of drum and instrument sounds, and + * store the audio buffers in arrays. */ - _loadAllDrumSounds () { + _loadAllSounds () { const loadingPromises = []; this.DRUM_INFO.forEach((drumInfo, index) => { - const promise = this._loadSound(drumInfo.fileName, index, this._drumBuffers); + const fileName = `drums/${drumInfo.fileName}`; + const promise = this._loadSound(fileName, index, this._drumBuffers); loadingPromises.push(promise); }); + this.INSTRUMENT_INFO.forEach((instrumentInfo, instrumentIndex) => { + this._instrumentBufferArrays[instrumentIndex] = []; + instrumentInfo.samples.forEach((sample, noteIndex) => { + const fileName = `instruments/${instrumentInfo.dirName}/${sample}`; + const promise = this._loadSound(fileName, noteIndex, this._instrumentBufferArrays[instrumentIndex]); + loadingPromises.push(promise); + }); + }); Promise.all(loadingPromises).then(() => { // @TODO: Update the extension status indicator. }); @@ -94,8 +109,10 @@ class Scratch3MusicBlocks { } /** - * An array of translatable drum names and corresponding audio file names. - * @type {array} + * An array of info about each drum. + * @type {object[]} an array of objects. + * @param {string} name - the translatable name to display in the drums menu. + * @param {string} fileName - the name of the audio file containing the drum sound. */ get DRUM_INFO () { return [ @@ -175,94 +192,134 @@ class Scratch3MusicBlocks { } /** - * An array of translatable instrument names and corresponding audio file names. - * @type {array} + * An array of info about each instrument. + * @type {object[]} an array of objects. + * @param {string} name - the translatable name to display in the instruments menu. + * @param {string} dirName - the name of the directory containing audio samples for this instrument. + * @param {number} [releaseTime] - an optional duration for the release portion of each note. + * @param {number[]} samples - an array of numbers representing the MIDI note number for each + * sampled sound used to play this instrument. */ get INSTRUMENT_INFO () { return [ { name: '(1) Piano', - fileName: '1-piano' + dirName: '1-piano', + releaseTime: 0.5, + samples: [24, 36, 48, 60, 72, 84, 96, 108] }, { name: '(2) Electric Piano', - fileName: '2-electric-piano' + dirName: '2-electric-piano', + releaseTime: 0.5, + samples: [60] }, { name: '(3) Organ', - fileName: '3-organ' + dirName: '3-organ', + releaseTime: 0.5, + samples: [60] }, { name: '(4) Guitar', - fileName: '4-guitar' + dirName: '4-guitar', + releaseTime: 0.5, + samples: [60] }, { name: '(5) Electric Guitar', - fileName: '5-electric-guitar' + dirName: '5-electric-guitar', + releaseTime: 0.5, + samples: [60] }, { name: '(6) Bass', - fileName: '6-bass' + dirName: '6-bass', + releaseTime: 0.25, + samples: [36, 48] }, { name: '(7) Pizzicato', - fileName: '7-pizzicato' + dirName: '7-pizzicato', + releaseTime: 0.25, + samples: [60] }, { name: '(8) Cello', - fileName: '8-cello' + dirName: '8-cello', + releaseTime: 0.1, + samples: [36, 48, 60] }, { name: '(9) Trombone', - fileName: '9-trombone' + dirName: '9-trombone', + samples: [36, 48, 60] }, { name: '(10) Clarinet', - fileName: '10-clarinet' + dirName: '10-clarinet', + samples: [48, 60] }, { name: '(11) Saxophone', - fileName: '11-saxophone' + dirName: '11-saxophone', + samples: [36, 60, 84] }, { name: '(12) Flute', - fileName: '12-flute' + dirName: '12-flute', + samples: [60, 72] }, { name: '(13) Wooden Flute', - fileName: '13-wooden-flute' + dirName: '13-wooden-flute', + samples: [60, 72] }, { name: '(14) Bassoon', - fileName: '14-bassoon' + dirName: '14-bassoon', + samples: [36, 48, 60] }, { name: '(15) Choir', - fileName: '15-choir' + dirName: '15-choir', + releaseTime: 0.25, + samples: [48, 60, 72] }, { name: '(16) Vibraphone', - fileName: '16-vibraphone' + dirName: '16-vibraphone', + releaseTime: 0.5, + samples: [60, 72] }, { name: '(17) Music Box', - fileName: '17-music-box' + dirName: '17-music-box', + releaseTime: 0.25, + samples: [60] }, { name: '(18) Steel Drum', - fileName: '18-steel-drum' + dirName: '18-steel-drum', + releaseTime: 0.5, + samples: [60] }, { name: '(19) Marimba', - fileName: '19-marimba' + dirName: '19-marimba', + samples: [60] }, { name: '(20) Synth Lead', - fileName: '20-synth-lead' + dirName: '20-synth-lead', + releaseTime: 0.1, + samples: [60] }, { name: '(21) Synth Pad', - fileName: '21-synth-pad' + dirName: '21-synth-pad', + releaseTime: 0.25, + samples: [60] } ]; } @@ -290,7 +347,7 @@ class Scratch3MusicBlocks { * @type {{min: number, max: number}} */ static get MIDI_NOTE_RANGE () { - return {min: 36, max: 96}; // C2 to C7 + return {min: 0, max: 130}; } /** @@ -457,19 +514,21 @@ class Scratch3MusicBlocks { * @private */ _playDrumNum (util, drumNum) { + if (util.runtime.audioEngine === null) return; if (util.target.audioPlayer === null) return; // If we're playing too many sounds, do not play the drum sound. - if (this._drumConcurrencyCounter > Scratch3MusicBlocks.CONCURRENCY_LIMIT) { + if (this._concurrencyCounter > Scratch3MusicBlocks.CONCURRENCY_LIMIT) { return; } const outputNode = util.target.audioPlayer.getInputNode(); - const bufferSource = this.runtime.audioEngine.audioContext.createBufferSource(); + const context = util.runtime.audioEngine.audioContext; + const bufferSource = context.createBufferSource(); bufferSource.buffer = this._drumBuffers[drumNum]; bufferSource.connect(outputNode); bufferSource.start(); - this._drumConcurrencyCounter++; + this._concurrencyCounter++; bufferSource.onended = () => { - this._drumConcurrencyCounter--; + this._concurrencyCounter--; }; } @@ -491,6 +550,7 @@ class Scratch3MusicBlocks { /** * Play a note using the current musical instrument for some number of beats. + * This function processes the arguments, and handles the timing of the block's execution. * @param {object} args - the block arguments. * @param {object} util - utility object provided by the runtime. * @property {number} NOTE - the pitch of the note to play, interpreted as a MIDI note number. @@ -503,17 +563,110 @@ class Scratch3MusicBlocks { Scratch3MusicBlocks.MIDI_NOTE_RANGE.min, Scratch3MusicBlocks.MIDI_NOTE_RANGE.max); let beats = Cast.toNumber(args.BEATS); beats = this._clampBeats(beats); - const musicState = this._getMusicState(util.target); - const inst = musicState.currentInstrument; - if (typeof this.runtime.audioEngine !== 'undefined') { - this.runtime.audioEngine.playNoteForBeatsWithInstAndVol(note, beats, inst, 100); - } - this._startStackTimer(util, this._beatsToSec(beats)); + // If the duration is 0, do not play the note. In Scratch 2.0, "play drum for 0 beats" plays the drum, + // but "play note for 0 beats" is silent. + if (beats === 0) return; + + const durationSec = this._beatsToSec(beats); + + this._playNote(util, note, durationSec); + + this._startStackTimer(util, durationSec); } else { this._checkStackTimer(util); } } + /** + * Play a note using the current instrument for a duration in seconds. + * This function actually plays the sound, and handles the timing of the sound, including the + * "release" portion of the sound, which continues briefly after the block execution has finished. + * @param {object} util - utility object provided by the runtime. + * @param {number} note - the pitch of the note to play, interpreted as a MIDI note number. + * @param {number} durationSec - the duration in seconds to play the note. + * @private + */ + _playNote (util, note, durationSec) { + if (util.runtime.audioEngine === null) return; + if (util.target.audioPlayer === null) return; + + // If we're playing too many sounds, do not play the note. + if (this._concurrencyCounter > Scratch3MusicBlocks.CONCURRENCY_LIMIT) { + return; + } + + // Determine which of the audio samples for this instrument to play + const musicState = this._getMusicState(util.target); + const inst = musicState.currentInstrument; + const instrumentInfo = this.INSTRUMENT_INFO[inst]; + const sampleArray = instrumentInfo.samples; + const sampleIndex = this._selectSampleIndexForNote(note, sampleArray); + + // Create the audio buffer to play the note, and set its pitch + const context = util.runtime.audioEngine.audioContext; + const bufferSource = context.createBufferSource(); + bufferSource.buffer = this._instrumentBufferArrays[inst][sampleIndex]; + const sampleNote = sampleArray[sampleIndex]; + bufferSource.playbackRate.value = this._ratioForPitchInterval(note - sampleNote); + + // Create a gain node for this note, and connect it to the sprite's audioPlayer. + const gainNode = context.createGain(); + bufferSource.connect(gainNode); + const outputNode = util.target.audioPlayer.getInputNode(); + gainNode.connect(outputNode); + + // Start playing the note + bufferSource.start(); + + // Schedule the release of the note, ramping its gain down to zero, + // and then stopping the sound. + let releaseDuration = this.INSTRUMENT_INFO[inst].releaseTime; + if (typeof releaseDuration === 'undefined') { + releaseDuration = 0.01; + } + const releaseStart = context.currentTime + durationSec; + const releaseEnd = releaseStart + releaseDuration; + gainNode.gain.setValueAtTime(1, releaseStart); + gainNode.gain.linearRampToValueAtTime(0.0001, releaseEnd); + bufferSource.stop(releaseEnd); + + // Update the concurrency counter + this._concurrencyCounter++; + bufferSource.onended = () => { + this._concurrencyCounter--; + }; + } + + /** + * The samples array for each instrument is the set of pitches of the available audio samples. + * This function selects the best one to use to play a given input note, and returns its index + * in the samples array. + * @param {number} note - the input note to select a sample for. + * @param {number[]} samples - an array of the pitches of the available samples. + * @return {index} the index of the selected sample in the samples array. + * @private + */ + _selectSampleIndexForNote (note, samples) { + // Step backwards through the array of samples, i.e. in descending pitch, in order to find + // the sample that is the closest one below (or matching) the pitch of the input note. + for (let i = samples.length - 1; i >= 0; i--) { + if (note >= samples[i]) { + return i; + } + } + return 0; + } + + /** + * Calcuate the frequency ratio for a given musical interval. + * @param {number} interval - the pitch interval to convert. + * @return {number} a ratio corresponding to the input interval. + * @private + */ + _ratioForPitchInterval (interval) { + return Math.pow(2, (interval / 12)); + } + /** * Clamp a duration in beats to the allowed min and max duration. * @param {number} beats - a duration in beats. @@ -574,16 +727,14 @@ class Scratch3MusicBlocks { * @param {object} args - the block arguments. * @param {object} util - utility object provided by the runtime. * @property {int} INSTRUMENT - the number of the instrument to select. - * @return {Promise} - a promise which will resolve once the instrument has loaded. */ setInstrument (args, util) { const musicState = this._getMusicState(util.target); let instNum = Cast.toNumber(args.INSTRUMENT); + instNum = Math.round(instNum); instNum -= 1; // instruments are one-indexed - if (typeof this.runtime.audioEngine === 'undefined') return; - instNum = MathUtil.wrapClamp(instNum, 0, this.runtime.audioEngine.numInstruments - 1); + instNum = MathUtil.wrapClamp(instNum, 0, this.INSTRUMENT_INFO.length - 1); musicState.currentInstrument = instNum; - return this.runtime.audioEngine.instrumentPlayer.loadInstrument(musicState.currentInstrument); } /** diff --git a/test/unit/extension_music.js b/test/unit/extension_music.js index b9e3c8af..93106297 100644 --- a/test/unit/extension_music.js +++ b/test/unit/extension_music.js @@ -1,17 +1,8 @@ const test = require('tap').test; const Music = require('../../src/extensions/scratch3_music/index.js'); -let playedDrum; -let playedInstrument; -const runtime = { - audioEngine: { - numInstruments: 3, - instrumentPlayer: { - loadInstrument: instrument => (playedInstrument = instrument) - } - } -}; +const runtime = Object.create(null); + const blocks = new Music(runtime); -blocks._playDrumNum = (util, drum) => (playedDrum = drum); const util = { stackFrame: Object.create(null), @@ -22,6 +13,10 @@ const util = { }; test('playDrum uses 1-indexing and wrap clamps', t => { + // Stub playDrumNum + let playedDrum; + blocks._playDrumNum = (_util, drum) => (playedDrum = drum); + let args = {DRUM: 1}; blocks.playDrumForBeats(args, util); t.strictEqual(playedDrum, 0); @@ -35,15 +30,16 @@ test('playDrum uses 1-indexing and wrap clamps', t => { test('setInstrument uses 1-indexing and wrap clamps', t => { // Stub getMusicState - blocks._getMusicState = () => ({}); + const state = {currentInstrument: 0}; + blocks._getMusicState = () => state; let args = {INSTRUMENT: 1}; blocks.setInstrument(args, util); - t.strictEqual(playedInstrument, 0); + t.strictEqual(state.currentInstrument, 0); - args = {INSTRUMENT: runtime.audioEngine.numInstruments + 1}; + args = {INSTRUMENT: blocks.INSTRUMENT_INFO.length + 1}; blocks.setInstrument(args, util); - t.strictEqual(playedInstrument, 0); + t.strictEqual(state.currentInstrument, 0); t.end(); }); From fc61f9e547820a92a4346cf7b3cc96f161c21697 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 21 Nov 2017 11:45:11 -0500 Subject: [PATCH 628/721] Select target after drag end --- src/virtual-machine.js | 23 +++++++++++++++++---- test/unit/virtual-machine.js | 39 ++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 994de986..263248b6 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -36,6 +36,13 @@ class VirtualMachine extends EventEmitter { * @type {Target} */ this.editingTarget = null; + + /** + * The currently dragging target, for redirecting IO data. + * @type {Target} + */ + this._dragTarget = null; + // Runtime emits are passed along as VM emits. this.runtime.on(Runtime.SCRIPT_GLOW_ON, glowData => { this.emit(Runtime.SCRIPT_GLOW_ON, glowData); @@ -692,8 +699,8 @@ class VirtualMachine extends EventEmitter { startDrag (targetId) { const target = this.runtime.getTargetById(targetId); if (target) { + this._dragTarget = target; target.startDrag(); - this.setEditingTarget(target.id); } } @@ -703,15 +710,23 @@ class VirtualMachine extends EventEmitter { */ stopDrag (targetId) { const target = this.runtime.getTargetById(targetId); - if (target) target.stopDrag(); + if (target) { + this._dragTarget = null; + target.stopDrag(); + this.setEditingTarget(target.id); + } } /** - * Post/edit sprite info for the current editing target. + * Post/edit sprite info for the current editing target or the drag target. * @param {object} data An object with sprite info data to set. */ postSpriteInfo (data) { - this.editingTarget.postSpriteInfo(data); + if (this._dragTarget) { + this._dragTarget.postSpriteInfo(data); + } else { + this.editingTarget.postSpriteInfo(data); + } } } diff --git a/test/unit/virtual-machine.js b/test/unit/virtual-machine.js index f0887039..40bd6bb5 100644 --- a/test/unit/virtual-machine.js +++ b/test/unit/virtual-machine.js @@ -316,3 +316,42 @@ test('emitWorkspaceUpdate', t => { t.notEqual(xml.indexOf('blocks'), -1); t.end(); }); + +test('drag IO redirect', t => { + const vm = new VirtualMachine(); + const sprite1Info = []; + const sprite2Info = []; + vm.runtime.targets = [ + { + id: 'sprite1', + postSpriteInfo: data => sprite1Info.push(data) + }, { + id: 'sprite2', + postSpriteInfo: data => sprite2Info.push(data), + startDrag: () => {}, + stopDrag: () => {} + } + ]; + vm.editingTarget = vm.runtime.targets[0]; + // Stub emitWorkspace/TargetsUpdate, it needs data we don't care about here + vm.emitWorkspaceUpdate = () => null; + vm.emitTargetsUpdate = () => null; + + // postSpriteInfo should go to the editing target by default`` + vm.postSpriteInfo('sprite1 info'); + t.equal(sprite1Info[0], 'sprite1 info'); + + // postSprite info goes to the drag target if it exists + vm.startDrag('sprite2'); + vm.postSpriteInfo('sprite2 info'); + t.equal(sprite2Info[0], 'sprite2 info'); + + // stop drag should set the editing target + vm.stopDrag('sprite2'); + t.equal(vm.editingTarget.id, 'sprite2'); + + // Then postSpriteInfo should continue posting to the new editing target + vm.postSpriteInfo('sprite2 info 2'); + t.equal(sprite2Info[1], 'sprite2 info 2'); + t.end(); +}); From 0a15190b858bfe2d2ea7810fdb15af53a0a423c1 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 21 Nov 2017 16:48:48 -0500 Subject: [PATCH 629/721] Broadcast message functionality (works with creating new messages, and switching back and forth between them as well). This commit includes a temporary workaround for the issue where the default broadcast message, 'message1' wasn't triggering a var_create event (filed in LLK/scratch-blocks#1258). --- src/blocks/scratch3_event.js | 10 ++++++++-- src/engine/blocks.js | 3 ++- src/engine/execute.js | 13 +++++++++++++ src/engine/target.js | 19 ++++++++++++++++++- src/engine/variable.js | 11 +++++++++++ 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/blocks/scratch3_event.js b/src/blocks/scratch3_event.js index 8687141c..1bf798f4 100644 --- a/src/blocks/scratch3_event.js +++ b/src/blocks/scratch3_event.js @@ -56,14 +56,20 @@ class Scratch3EventBlocks { } broadcast (args, util) { - const broadcastOption = Cast.toString(args.BROADCAST_OPTION); + // TODO KARISHMA adding lookupOrCreate here temporarily (LLK/scratch-blocks#1258) + util.target.lookupOrCreateBroadcastMsg(args.BROADCAST_OPTION.id, + args.BROADCAST_OPTION.name); + const broadcastOption = Cast.toString(args.BROADCAST_OPTION.name); util.startHats('event_whenbroadcastreceived', { BROADCAST_OPTION: broadcastOption }); } broadcastAndWait (args, util) { - const broadcastOption = Cast.toString(args.BROADCAST_OPTION); + // TODO KARISHMA adding lookupOrCreate here temporarily (LLK/scratch-blocks#1258) + util.target.lookupOrCreateBroadcastMsg(args.BROADCAST_OPTION.id, + args.BROADCAST_OPTION.name); + const broadcastOption = Cast.toString(args.BROADCAST_OPTION.name); // Have we run before, starting threads? if (!util.stackFrame.startedThreads) { // No - start hats for this broadcast. diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 8776c6f6..9117e991 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -365,7 +365,8 @@ class Blocks { case 'field': // Update block value if (!block.fields[args.name]) return; - if (args.name === 'VARIABLE' || args.name === 'LIST') { + if (args.name === 'VARIABLE' || args.name === 'LIST' || + args.name === 'BROADCAST_OPTION') { // Get variable name using the id in args.value. const variable = optRuntime.getEditingTarget().lookupVariableById(args.value); if (variable) { diff --git a/src/engine/execute.js b/src/engine/execute.js index f5acf909..3d2e5aa6 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -171,6 +171,19 @@ const execute = function (sequencer, thread) { if (!fields.hasOwnProperty(fieldName)) continue; if (fieldName === 'VARIABLE' || fieldName === 'LIST') { argValues[fieldName] = fields[fieldName].id; + } else if (fieldName === 'BROADCAST_OPTION') { + // TODO KARISHMA (scratch-blocks #1258) this will change when we + // upstream blockly issue #1395 is fixed. + + // we need both the id and the name here, because + // this object eventually gets passed to scratch3_event.broadcast + // which will create the message for the first time if one with the + // given id doesn't exist + // (e.g. default broadcast message, 'message1') + argValues[fieldName] = { + id: fields[fieldName].id, + name: fields[fieldName].value + }; } else { argValues[fieldName] = fields[fieldName].value; } diff --git a/src/engine/target.js b/src/engine/target.js index 6a5c8d12..9a6ad728 100644 --- a/src/engine/target.js +++ b/src/engine/target.js @@ -92,6 +92,23 @@ class Target extends EventEmitter { return newVariable; } + /** + * Look up a broadcast message object, and create it if one doesn't exist. + * @param {string} id Id of the variable. + * @param {string} name Name of the variable. + * @return {!Variable} Variable object. + */ + lookupOrCreateBroadcastMsg (id, name) { + debugger; + const broadcastMsg = this.lookupVariableById(id); + if (broadcastMsg) return broadcastMsg; + // No variable with this name exists - create it locally. + const newBroadcastMsg = new Variable(id, name, + Variable.BROADCAST_MESSAGE_TYPE, false); + this.variables[id] = newBroadcastMsg; + return newBroadcastMsg; + } + /** * Look up a variable object. * Search begins for local variables; then look for globals. @@ -134,7 +151,7 @@ class Target extends EventEmitter { * dictionary of variables. * @param {string} id Id of variable * @param {string} name Name of variable. - * @param {string} type Type of variable, '' or 'list' + * @param {string} type Type of variable, '', 'broadcast_msg', or 'list' */ createVariable (id, name, type) { if (!this.variables.hasOwnProperty(id)) { diff --git a/src/engine/variable.js b/src/engine/variable.js index d75c0b82..4aad9ffc 100644 --- a/src/engine/variable.js +++ b/src/engine/variable.js @@ -25,6 +25,9 @@ class Variable { case Variable.LIST_TYPE: this.value = []; break; + case Variable.BROADCAST_MESSAGE_TYPE: + this.value = this.name; + break; default: throw new Error(`Invalid variable type: ${this.type}`); } @@ -51,6 +54,14 @@ class Variable { static get LIST_TYPE () { return 'list'; } + + /** + * Type representation for list variables. + * @const {string} + */ + static get BROADCAST_MESSAGE_TYPE () { + return 'broadcast_msg'; + } } module.exports = Variable; From f610816e72284677b3c00ef57cc888eea43d451b Mon Sep 17 00:00:00 2001 From: sjhuang26 Date: Fri, 24 Nov 2017 21:28:48 -0500 Subject: [PATCH 630/721] Added wasStage parameter --- src/blocks/scratch3_sensing.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index fa129f6c..19357c88 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -63,9 +63,9 @@ class Scratch3SensingBlocks { this._answer = answer; const questionObj = this._questionList.shift(); if (questionObj) { - const [_question, resolve, target, wasVisible] = questionObj; - // If the target was visible when asked, hide the say bubble. - if (wasVisible) { + const [_question, resolve, target, wasVisible, wasStage] = questionObj; + // If the target was visible when asked, hide the say bubble unless the target was the stage. + if (wasVisible && !wasStage) { this.runtime.emit('SAY', target, 'say', ''); } resolve(); @@ -73,16 +73,16 @@ class Scratch3SensingBlocks { } } - _enqueueAsk (question, resolve, target, wasVisible) { - this._questionList.push([question, resolve, target, wasVisible]); + _enqueueAsk (question, resolve, target, wasVisible, wasStage) { + this._questionList.push([question, resolve, target, wasVisible, wasStage]); } _askNextQuestion () { if (this._questionList.length > 0) { - const [question, _resolve, target, wasVisible] = this._questionList[0]; + const [question, _resolve, target, wasVisible, wasStage] = this._questionList[0]; // If the target is visible, emit a blank question and use the - // say event to trigger a bubble. - if (wasVisible) { + // say event to trigger a bubble unless the target was the stage. + if (wasVisible && !wasStage) { this.runtime.emit('SAY', target, 'say', question); this.runtime.emit('QUESTION', ''); } else { @@ -100,7 +100,7 @@ class Scratch3SensingBlocks { const _target = util.target; return new Promise(resolve => { const isQuestionAsked = this._questionList.length > 0; - this._enqueueAsk(args.QUESTION, resolve, _target, _target.visible); + this._enqueueAsk(args.QUESTION, resolve, _target, _target.visible, _target.isStage); if (!isQuestionAsked) { this._askNextQuestion(); } From 7e87f314fb3cfd6ac8d6aeb3f6b15fcf41542281 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 27 Nov 2017 10:06:42 -0500 Subject: [PATCH 631/721] Convert menu values to string --- src/extensions/scratch3_music/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index 9a200796..e170cf39 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -103,7 +103,7 @@ class Scratch3MusicBlocks { return info.map((entry, index) => { const obj = {}; obj.text = entry.name; - obj.value = index + 1; + obj.value = String(index + 1); return obj; }); } From b78bdb4c934feeab429bac7c780f54d193c9ac69 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 27 Nov 2017 15:00:53 -0500 Subject: [PATCH 632/721] Address feedback from review --- src/io/deviceManager.js | 2 +- test/unit/io_deviceManager.js | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/io/deviceManager.js b/src/io/deviceManager.js index 2ba018ce..9f7075e5 100644 --- a/src/io/deviceManager.js +++ b/src/io/deviceManager.js @@ -319,7 +319,7 @@ class DeviceManager { json: {} }, (err, res, body) => { if (err) return reject(err); - if (res.statusCode !== 200) reject(body); + if (res.statusCode !== 200) return reject(body); resolve(body); }); }); diff --git a/test/unit/io_deviceManager.js b/test/unit/io_deviceManager.js index f4d32d06..499b995f 100644 --- a/test/unit/io_deviceManager.js +++ b/test/unit/io_deviceManager.js @@ -24,11 +24,16 @@ test('list', t => { const deviceManager = new DeviceManager(); deviceManager .list('test', 'test', null) - .catch(err => { - t.true((typeof err === 'undefined') || (typeof err === 'object')); - }) - .then(body => { - t.true((typeof body === 'undefined') || (typeof body === 'object')); - t.end(); - }); + .then( + body => { + // SDM is running + t.type(body, 'object'); + t.end(); + }, + err => { + // If SDM is not running error is expected, continue + t.true(typeof err === 'object' || typeof err === 'undefined'); + t.end(); + } + ); }); From 12acdefb5adafe0e683d0e192282bbea02b416e5 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Mon, 27 Nov 2017 19:15:05 -0800 Subject: [PATCH 633/721] Add "browser" field to package.json This informs webpack (say, in `scratch-gui`) about the web-targeted build output so that it can resolve dependencies correctly. This fixes build problems that became evident after merging #805. --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 98c0f6c3..2ccc8089 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "url": "git+ssh://git@github.com/LLK/scratch-vm.git" }, "main": "./dist/node/scratch-vm.js", + "browser": "./dist/web/scratch-vm.js", "scripts": { "build": "webpack --progress --colors --bail", "coverage": "tap ./test/{unit,integration}/*.js --coverage --coverage-report=lcov", From 88dada5cb86ce507e0fe52345b377c8d879941ac Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Mon, 27 Nov 2017 20:00:03 -0800 Subject: [PATCH 634/721] Use UMD for web ouput UMD output seems to be working well for the storage module; this change adjusts the web-targeted build outputs of this module to use it as well. Also, common output properties are now specified in the base config and removed from each individual config. --- webpack.config.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index f2d4c880..0fc99b26 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -10,6 +10,10 @@ const base = { port: process.env.PORT || 8073 }, devtool: 'cheap-module-source-map', + output: { + library: 'VirtualMachine', + filename: '[name].js' + }, module: { rules: [{ test: /\.js$/, @@ -37,8 +41,8 @@ module.exports = [ 'scratch-vm.min': './src/index.js' }, output: { - path: path.resolve(__dirname, 'dist/web'), - filename: '[name].js' + libraryTarget: 'umd', + path: path.resolve('dist', 'web') }, module: { rules: base.module.rules.concat([ @@ -56,10 +60,8 @@ module.exports = [ 'scratch-vm': './src/index.js' }, output: { - library: 'VirtualMachine', libraryTarget: 'commonjs2', - path: path.resolve(__dirname, 'dist/node'), - filename: '[name].js' + path: path.resolve('dist', 'node') }, plugins: base.plugins.concat([ new CopyWebpackPlugin([{ @@ -87,8 +89,8 @@ module.exports = [ ] }, output: { - path: path.resolve(__dirname, 'playground'), - filename: '[name].js' + libraryTarget: 'umd', + path: path.resolve('playground') }, module: { rules: base.module.rules.concat([ From a71e06af433e27d2c36429476740e6196ef71a52 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 28 Nov 2017 12:29:45 -0500 Subject: [PATCH 635/721] =?UTF-8?q?Clones=20get=20a=20reference=20to=20par?= =?UTF-8?q?ent=E2=80=99s=20audioPlayer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sprites/rendered-target.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 653bc4ef..9e181b95 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -137,7 +137,11 @@ class RenderedTarget extends Target { */ this.audioPlayer = null; if (this.runtime && this.runtime.audioEngine) { - this.audioPlayer = this.runtime.audioEngine.createPlayer(); + if (this.isOriginal) { + this.audioPlayer = this.runtime.audioEngine.createPlayer(); + } else { + this.audioPlayer = this.sprite.clones[0].audioPlayer; + } } } From 773c167987258a098ddc938949ff66ce17d98468 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 28 Nov 2017 13:15:48 -0500 Subject: [PATCH 636/721] Check for instrument sample before playing --- src/extensions/scratch3_music/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index e170cf39..807c3f79 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -602,6 +602,10 @@ class Scratch3MusicBlocks { const sampleArray = instrumentInfo.samples; const sampleIndex = this._selectSampleIndexForNote(note, sampleArray); + // If the audio sample has not loaded yet, bail out + if (typeof this._instrumentBufferArrays[inst] === 'undefined') return; + if (typeof this._instrumentBufferArrays[inst][sampleIndex] === 'undefined') return; + // Create the audio buffer to play the note, and set its pitch const context = util.runtime.audioEngine.audioContext; const bufferSource = context.createBufferSource(); From b674a0c047afb91083db2369ce9e9c3a3c6ade9b Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 28 Nov 2017 22:33:27 -0500 Subject: [PATCH 637/721] Code cleanup and refactoring, and getting scratch3_event broadcast functionality to look up the variable name instead of getting it ad-hoc. --- src/blocks/scratch3_data.js | 33 ++++++++++++++++++++++----------- src/blocks/scratch3_event.js | 14 ++++++-------- src/engine/execute.js | 13 ++----------- src/engine/target.js | 1 - 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/blocks/scratch3_data.js b/src/blocks/scratch3_data.js index 8ec4f124..0a382e28 100644 --- a/src/blocks/scratch3_data.js +++ b/src/blocks/scratch3_data.js @@ -30,24 +30,28 @@ class Scratch3DataBlocks { } getVariable (args, util) { - const variable = util.target.lookupOrCreateVariable(args.VARIABLE); + const variable = util.target.lookupOrCreateVariable( + args.VARIABLE.id, args.VARIABLE.name); return variable.value; } setVariableTo (args, util) { - const variable = util.target.lookupOrCreateVariable(args.VARIABLE); + const variable = util.target.lookupOrCreateVariable( + args.VARIABLE.id, args.VARIABLE.name); variable.value = args.VALUE; } changeVariableBy (args, util) { - const variable = util.target.lookupOrCreateVariable(args.VARIABLE); + const variable = util.target.lookupOrCreateVariable( + args.VARIABLE.id, args.VARIABLE.name); const castedValue = Cast.toNumber(variable.value); const dValue = Cast.toNumber(args.VALUE); variable.value = castedValue + dValue; } getListContents (args, util) { - const list = util.target.lookupOrCreateList(args.LIST); + const list = util.target.lookupOrCreateList( + args.LIST.id, args.LIST.name); // Determine if the list is all single letters. // If it is, report contents joined together with no separator. // If it's not, report contents joined together with a space. @@ -68,12 +72,14 @@ class Scratch3DataBlocks { } addToList (args, util) { - const list = util.target.lookupOrCreateList(args.LIST); + const list = util.target.lookupOrCreateList( + args.LIST.id, args.LIST.name); list.value.push(args.ITEM); } deleteOfList (args, util) { - const list = util.target.lookupOrCreateList(args.LIST); + const list = util.target.lookupOrCreateList( + args.LIST.id, args.LIST.name); const index = Cast.toListIndex(args.INDEX, list.value.length); if (index === Cast.LIST_INVALID) { return; @@ -86,7 +92,8 @@ class Scratch3DataBlocks { insertAtList (args, util) { const item = args.ITEM; - const list = util.target.lookupOrCreateList(args.LIST); + const list = util.target.lookupOrCreateList( + args.LIST.id, args.LIST.name); const index = Cast.toListIndex(args.INDEX, list.value.length + 1); if (index === Cast.LIST_INVALID) { return; @@ -96,7 +103,8 @@ class Scratch3DataBlocks { replaceItemOfList (args, util) { const item = args.ITEM; - const list = util.target.lookupOrCreateList(args.LIST); + const list = util.target.lookupOrCreateList( + args.LIST.id, args.LIST.name); const index = Cast.toListIndex(args.INDEX, list.value.length); if (index === Cast.LIST_INVALID) { return; @@ -105,7 +113,8 @@ class Scratch3DataBlocks { } getItemOfList (args, util) { - const list = util.target.lookupOrCreateList(args.LIST); + const list = util.target.lookupOrCreateList( + args.LIST.id, args.LIST.name); const index = Cast.toListIndex(args.INDEX, list.value.length); if (index === Cast.LIST_INVALID) { return ''; @@ -114,13 +123,15 @@ class Scratch3DataBlocks { } lengthOfList (args, util) { - const list = util.target.lookupOrCreateList(args.LIST); + const list = util.target.lookupOrCreateList( + args.LIST.id, args.LIST.name); return list.value.length; } listContainsItem (args, util) { const item = args.ITEM; - const list = util.target.lookupOrCreateList(args.LIST); + const list = util.target.lookupOrCreateList( + args.LIST.id, args.LIST.name); if (list.value.indexOf(item) >= 0) { return true; } diff --git a/src/blocks/scratch3_event.js b/src/blocks/scratch3_event.js index 1bf798f4..5fbaa5b7 100644 --- a/src/blocks/scratch3_event.js +++ b/src/blocks/scratch3_event.js @@ -56,20 +56,18 @@ class Scratch3EventBlocks { } broadcast (args, util) { - // TODO KARISHMA adding lookupOrCreate here temporarily (LLK/scratch-blocks#1258) - util.target.lookupOrCreateBroadcastMsg(args.BROADCAST_OPTION.id, - args.BROADCAST_OPTION.name); - const broadcastOption = Cast.toString(args.BROADCAST_OPTION.name); + const broadcastVar = util.runtime.getTargetForStage().lookupOrCreateBroadcastMsg( + args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name); + const broadcastOption = broadcastVar.name; util.startHats('event_whenbroadcastreceived', { BROADCAST_OPTION: broadcastOption }); } broadcastAndWait (args, util) { - // TODO KARISHMA adding lookupOrCreate here temporarily (LLK/scratch-blocks#1258) - util.target.lookupOrCreateBroadcastMsg(args.BROADCAST_OPTION.id, - args.BROADCAST_OPTION.name); - const broadcastOption = Cast.toString(args.BROADCAST_OPTION.name); + const broadcastVar = util.runtime.getTargetForStage().lookupOrCreateBroadcastMsg( + args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name); + const broadcastOption = broadcastVar.name; // Have we run before, starting threads? if (!util.stackFrame.startedThreads) { // No - start hats for this broadcast. diff --git a/src/engine/execute.js b/src/engine/execute.js index 3d2e5aa6..4b1c0abf 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -169,17 +169,8 @@ const execute = function (sequencer, thread) { // Add all fields on this block to the argValues. for (const fieldName in fields) { if (!fields.hasOwnProperty(fieldName)) continue; - if (fieldName === 'VARIABLE' || fieldName === 'LIST') { - argValues[fieldName] = fields[fieldName].id; - } else if (fieldName === 'BROADCAST_OPTION') { - // TODO KARISHMA (scratch-blocks #1258) this will change when we - // upstream blockly issue #1395 is fixed. - - // we need both the id and the name here, because - // this object eventually gets passed to scratch3_event.broadcast - // which will create the message for the first time if one with the - // given id doesn't exist - // (e.g. default broadcast message, 'message1') + if (fieldName === 'VARIABLE' || fieldName === 'LIST' || + fieldName === 'BROADCAST_OPTION') { argValues[fieldName] = { id: fields[fieldName].id, name: fields[fieldName].value diff --git a/src/engine/target.js b/src/engine/target.js index 9a6ad728..39273fcc 100644 --- a/src/engine/target.js +++ b/src/engine/target.js @@ -99,7 +99,6 @@ class Target extends EventEmitter { * @return {!Variable} Variable object. */ lookupOrCreateBroadcastMsg (id, name) { - debugger; const broadcastMsg = this.lookupVariableById(id); if (broadcastMsg) return broadcastMsg; // No variable with this name exists - create it locally. From 80b4dab1048e3e1d6a65a96f6f1491fc567a0f99 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 29 Nov 2017 10:34:00 -0500 Subject: [PATCH 638/721] Decode the encoded argument information --- package.json | 1 + src/engine/mutation-adapter.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 2ccc8089..aa5de51a 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "babel-loader": "^7.0.0", "babel-preset-es2015": "^6.24.1", "copy-webpack-plugin": "4.2.1", + "decode-html": "2.0.0", "escape-html": "1.0.3", "eslint": "^4.5.0", "eslint-config-scratch": "^5.0.0", diff --git a/src/engine/mutation-adapter.js b/src/engine/mutation-adapter.js index c40052ff..d1984d30 100644 --- a/src/engine/mutation-adapter.js +++ b/src/engine/mutation-adapter.js @@ -1,4 +1,5 @@ const html = require('htmlparser2'); +const decodeHtml = require('decode-html'); /** * Convert a part of a mutation DOM to a mutation VM object, recursively. @@ -11,7 +12,7 @@ const mutatorTagToObject = function (dom) { obj.children = []; for (const prop in dom.attribs) { if (prop === 'xmlns') continue; - obj[prop] = dom.attribs[prop]; + obj[prop] = decodeHtml(dom.attribs[prop]); } for (let i = 0; i < dom.children.length; i++) { obj.children.push( From e5378d323d3eece585f8e48a356769eec529271a Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 29 Nov 2017 10:43:07 -0500 Subject: [PATCH 639/721] Adding unit tests for new lookupOrCreate funciton, fixing a bug that came up during testing (using lookupVariableById on a null target). Skipping integration tests that do not work right now because SB2 import of broadcast message blocks has not been implemented yet. --- src/engine/blocks.js | 9 ++++++++- test/integration/data.js | 4 +++- test/integration/event.js | 4 +++- test/integration/motion.js | 4 +++- test/unit/blocks_event.js | 14 ++++++++------ test/unit/engine_target.js | 29 +++++++++++++++++++++++++++++ 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 9117e991..ed0cc4d8 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -304,7 +304,14 @@ class Blocks { // Check if this variable exists on the current target or stage. // If not, create it on the stage. // TODO create global and local variables when UI provides a way. - if (!optRuntime.getEditingTarget().lookupVariableById(e.varId)) { + if (optRuntime.getEditingTarget()) { + if (!optRuntime.getEditingTarget().lookupVariableById(e.varId)) { + stage.createVariable(e.varId, e.varName, e.varType); + } + } else if (!stage.lookupVariableById(e.varId)) { + // Since getEditingTarget returned null, we now need to + // explicitly check if the stage has the variable, and + // create one if not. stage.createVariable(e.varId, e.varName, e.varType); } break; diff --git a/test/integration/data.js b/test/integration/data.js index c6cd71d1..63048fc7 100644 --- a/test/integration/data.js +++ b/test/integration/data.js @@ -7,7 +7,9 @@ const VirtualMachine = require('../../src/index'); const uri = path.resolve(__dirname, '../fixtures/data.sb2'); const project = extract(uri); -test('data', t => { +// TODO (#828) Need to re-activate this test after sb2 import works for broadcast +// blocks +test('data', {todo: true}, t => { const vm = new VirtualMachine(); vm.attachStorage(makeTestStorage()); diff --git a/test/integration/event.js b/test/integration/event.js index c4f3ee58..648f8cdd 100644 --- a/test/integration/event.js +++ b/test/integration/event.js @@ -7,7 +7,9 @@ const VirtualMachine = require('../../src/index'); const uri = path.resolve(__dirname, '../fixtures/event.sb2'); const project = extract(uri); -test('event', t => { +// TODO (#828) Need to re-activate this test after sb2 import works for broadcast +// blocks +test('event', {todo: true}, t => { const vm = new VirtualMachine(); vm.attachStorage(makeTestStorage()); diff --git a/test/integration/motion.js b/test/integration/motion.js index 689b2187..dbb24a11 100644 --- a/test/integration/motion.js +++ b/test/integration/motion.js @@ -7,7 +7,9 @@ const VirtualMachine = require('../../src/index'); const uri = path.resolve(__dirname, '../fixtures/motion.sb2'); const project = extract(uri); -test('motion', t => { +// TODO (#828) Need to re-activate this test after sb2 import works for broadcast +// blocks +test('motion', {todo: true}, t => { const vm = new VirtualMachine(); vm.attachStorage(makeTestStorage()); diff --git a/test/unit/blocks_event.js b/test/unit/blocks_event.js index 2e62125b..35a1f0b4 100644 --- a/test/unit/blocks_event.js +++ b/test/unit/blocks_event.js @@ -12,7 +12,7 @@ test('#760 - broadcastAndWait', t => { id: 'broadcastAndWaitBlock', fields: { BROADCAST_OPTION: { - id: 'BROADCAST_OPTION', + id: 'testBroadcastID', value: 'message' } }, @@ -30,7 +30,7 @@ test('#760 - broadcastAndWait', t => { id: 'receiveMessageBlock', fields: { BROADCAST_OPTION: { - id: 'BROADCAST_OPTION', + id: 'testBroadcastID', value: 'message' } }, @@ -51,15 +51,17 @@ test('#760 - broadcastAndWait', t => { b.createBlock(broadcastAndWaitBlock); b.createBlock(receiveMessageBlock); const tgt = new Target(rt, b); + tgt.isStage = true; rt.targets.push(tgt); let th = rt._pushThread('broadcastAndWaitBlock', t); const util = new BlockUtility(); util.sequencer = rt.sequencer; util.thread = th; + util.runtime = rt; // creates threads - e.broadcastAndWait({BROADCAST_OPTION: 'message'}, util); + e.broadcastAndWait({BROADCAST_OPTION: {id: 'testBroadcastID', name: 'message'}}, util); t.strictEqual(rt.threads.length, 2); t.strictEqual(rt.threads[1].topBlock, 'receiveMessageBlock'); // yields when some thread is active @@ -76,18 +78,18 @@ test('#760 - broadcastAndWait', t => { // restarts done threads that are in runtime threads th = rt._pushThread('broadcastAndWaitBlock', tgt); util.thread = th; - e.broadcastAndWait({BROADCAST_OPTION: 'message'}, util); + e.broadcastAndWait({BROADCAST_OPTION: {id: 'testBroadcastID', name: 'message'}}, util); t.strictEqual(rt.threads.length, 3); t.strictEqual(rt.threads[1].status, Thread.STATUS_RUNNING); t.strictEqual(th.status, Thread.STATUS_YIELD); // yields when some restarted thread is active th.status = Thread.STATUS_RUNNING; - e.broadcastAndWait({BROADCAST_OPTION: 'message'}, util); + e.broadcastAndWait({BROADCAST_OPTION: {id: 'testBroadcastID', name: 'message'}}, util); t.strictEqual(th.status, Thread.STATUS_YIELD); // does not yield once all threads are done th.status = Thread.STATUS_RUNNING; rt.threads[1].status = Thread.STATUS_DONE; - e.broadcastAndWait({BROADCAST_OPTION: 'message'}, util); + e.broadcastAndWait({BROADCAST_OPTION: {id: 'testBroadcastID', name: 'message'}}, util); t.strictEqual(th.status, Thread.STATUS_RUNNING); t.end(); diff --git a/test/unit/engine_target.js b/test/unit/engine_target.js index 70bf46e9..4f663906 100644 --- a/test/unit/engine_target.js +++ b/test/unit/engine_target.js @@ -173,3 +173,32 @@ test('lookupOrCreateList returns list if one with given id exists', t => { t.end(); }); + +test('lookupOrCreateBroadcastMsg creates a var if one does not exist', t => { + const target = new Target(); + const variables = target.variables; + + t.equal(Object.keys(variables).length, 0); + const broadcastVar = target.lookupOrCreateBroadcastMsg('foo', 'bar'); + t.equal(Object.keys(variables).length, 1); + t.equal(broadcastVar.id, 'foo'); + t.equal(broadcastVar.name, 'bar'); + + t.end(); +}); + +test('lookupOrCreateBroadcastMsg returns the var with given id if exists', t => { + const target = new Target(); + const variables = target.variables; + + t.equal(Object.keys(variables).length, 0); + target.createVariable('foo', 'bar', Variable.BROADCAST_MESSAGE_TYPE); + t.equal(Object.keys(variables).length, 1); + + const broadcastMsg = target.lookupOrCreateBroadcastMsg('foo', 'bar'); + t.equal(Object.keys(variables).length, 1); + t.equal(broadcastMsg.id, 'foo'); + t.equal(broadcastMsg.name, 'bar'); + + t.end(); +}); From 5a53323715f9cdf7b270d33dc2ec12babf0840ce Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 29 Nov 2017 12:00:00 -0500 Subject: [PATCH 640/721] Update to use explicit procedure names and ids --- src/blocks/scratch3_procedures.js | 8 ++++---- src/engine/block-utility.js | 4 ++-- src/engine/blocks.js | 9 +++++---- test/unit/blocks_procedures.js | 2 +- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/blocks/scratch3_procedures.js b/src/blocks/scratch3_procedures.js index 22e2f788..645e582e 100644 --- a/src/blocks/scratch3_procedures.js +++ b/src/blocks/scratch3_procedures.js @@ -27,7 +27,7 @@ class Scratch3ProcedureBlocks { call (args, util) { if (!util.stackFrame.executed) { const procedureCode = args.mutation.proccode; - const paramNames = util.getProcedureParamNames(procedureCode); + const [paramNames, paramIds] = util.getProcedureParamNamesAndIds(procedureCode); // If null, procedure could not be found, which can happen if custom // block is dragged between sprites without the definition. @@ -36,9 +36,9 @@ class Scratch3ProcedureBlocks { return; } - for (let i = 0; i < paramNames.length; i++) { - if (args.hasOwnProperty(`input${i}`)) { - util.pushParam(paramNames[i], args[`input${i}`]); + for (let i = 0; i < paramIds.length; i++) { + if (args.hasOwnProperty(paramIds[i])) { + util.pushParam(paramNames[i], args[paramIds[i]]); } } diff --git a/src/engine/block-utility.js b/src/engine/block-utility.js index 9fee1f7b..6ecab07d 100644 --- a/src/engine/block-utility.js +++ b/src/engine/block-utility.js @@ -97,8 +97,8 @@ class BlockUtility { * @param {string} procedureCode Procedure code for procedure to query. * @return {Array.} List of param names for a procedure. */ - getProcedureParamNames (procedureCode) { - return this.thread.target.blocks.getProcedureParamNames(procedureCode); + getProcedureParamNamesAndIds (procedureCode) { + return this.thread.target.blocks.getProcedureParamNamesAndIds(procedureCode); } /** diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 82c5e21c..5b330afc 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -206,7 +206,7 @@ class Blocks { * @param {?string} name Name of procedure to query. * @return {?Array.} List of param names for a procedure. */ - getProcedureParamNames (name) { + getProcedureParamNamesAndIds (name) { const cachedNames = this._cache.procedureParamNames[name]; if (typeof cachedNames !== 'undefined') { return cachedNames; @@ -217,9 +217,10 @@ class Blocks { const block = this._blocks[id]; if (block.opcode === 'procedures_prototype' && block.mutation.proccode === name) { - const paramNames = JSON.parse(block.mutation.argumentnames); - this._cache.procedureParamNames[name] = paramNames; - return paramNames; + const names = JSON.parse(block.mutation.argumentnames); + const ids = JSON.parse(block.mutation.argumentids); + this._cache.procedureParamNames[name] = [names, ids]; + return this._cache.procedureParamNames[name]; } } diff --git a/test/unit/blocks_procedures.js b/test/unit/blocks_procedures.js index cab5e04e..0285b23d 100644 --- a/test/unit/blocks_procedures.js +++ b/test/unit/blocks_procedures.js @@ -16,7 +16,7 @@ test('calling a custom block with no definition does not throw', t => { } }; const util = { - getProcedureParamNames: () => null, + getProcedureParamNamesAndIds: () => null, stackFrame: { executed: false } From 799a8ab7996717751cac24fcb29adeb007310839 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 29 Nov 2017 15:25:34 -0500 Subject: [PATCH 641/721] Update open and closed hi-hats --- .../assets/drums/5-open-hi-hat.mp3 | Bin 6414 -> 5998 bytes .../assets/drums/6-closed-hi-hat.mp3 | Bin 3338 -> 2555 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/extensions/scratch3_music/assets/drums/5-open-hi-hat.mp3 b/src/extensions/scratch3_music/assets/drums/5-open-hi-hat.mp3 index 2dc63fa30366ffeac6a86ef54c6cc95325258444..9e9e62b5599986b2005b0595bb8c56d0c2748994 100644 GIT binary patch literal 5998 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 VaSZ@&0DwdU0FeLB?El^1zW_(^OvC^H literal 6414 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 literal 3338 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 X74ieXNkx+n0{~iOCAggB|CWCP{dmH7 From bf5d489cda2253e10ae9da335a7ea9f303934d7a Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 29 Nov 2017 15:25:40 -0500 Subject: [PATCH 642/721] Update crash cymbal --- .../assets/drums/4-crash-cymbal.mp3 | Bin 17392 -> 53609 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/extensions/scratch3_music/assets/drums/4-crash-cymbal.mp3 b/src/extensions/scratch3_music/assets/drums/4-crash-cymbal.mp3 index 1522b05cb210a9399ae34afef7e139f68149986d..bf9c664793615bcb5f5422b770b02d70e3793802 100644 GIT binary patch literal 53609 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 From bcdc951eb1429d174761c4fd23dcafaa2c0237a6 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 29 Nov 2017 15:27:07 -0500 Subject: [PATCH 643/721] Update bass drum --- .../assets/drums/2-bass-drum.mp3 | Bin 4513 -> 1877 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/extensions/scratch3_music/assets/drums/2-bass-drum.mp3 b/src/extensions/scratch3_music/assets/drums/2-bass-drum.mp3 index 176d5bacceaa2d19957ad4de92a3e9b4f3ba59d6..bc25332748272a81fdaa8f751d4bf6a53a50f09b 100644 GIT binary patch literal 1877 zcmezWd%_V0bP$o5mkt!;2Vyn`2KG<}T{IAl4kn?2ljz_rI{1$UfOZA=IQqI8>seY9 zvFwC7$wWbg-Q1CZ@d3ykp^asish4MzyiIN6H~ggWCi!~hwAoUIqW6XRrfnLK>BvO!9VE>4;EkX zyBXB8t7Acd`Kp3@CKu!Fy1rR0l5yj{YqL~OWywG1#V7aP<69bjZfQctVYUm~TMN(B zEnGMM=)7&MvhN<X6-%t*J)Wres>}z0>`p zSaY&;j#pgmjBv?kx3`-_C~t8wTI5-}xi0%xT7w?1Im3pm#1nK zuD)<2KUC;p0B6+#&1Tg_3Cn^X1U0R4&q$jP+}SkcrM9)r=Yy@)yTh~o|1JQg1cAVI z-?uaJUU_eRS$df7Eu&g%Sz7G=`?>vwRD<^qMH|=e`-j;Mm%>R8` zMLNq?x1d)~RIbL`-%IrLTUk?e+|0|X>1|Y7^5i+mpVPbdwna`8jr%+O+ywpI`Q`uX z*E~x-!7k3+y5@N`pBUQ>RTTxwWlD>f@I~ zr%uJKR&}{<6mmB5!og)(Cx0zBs@dDE7Wvp{2^ZH+qho4Jg@~-SfN6~@CdO;7y=-rM_wTjZwe?>gP3u$iDUGpQ_xA4l(x$o3ecC^4Idd+l zshOc|d5H1GhhK6uZiXn%P+w?h=i#-8xpa!anavYU9X81|VYz$iTS5QEve5H$Yj5;j z_|+1@sU-UFrq1t7VcXPTXH)hLnaaeZuMAUM7E1C2E_a&Uy<4mN?o8+1);ON1jhJvlXGL7-jElY4zjIW{Y!p>*SuuXAiz^vc(tT4|+u z$RLsbS!0pWisQ+JRm!v3Oy*3_|H~4uj zb27@XP4kMd8COSFFQ*(YM@ztj(9;s1mP`q4>ThYEwfv;<|2H--ydSf-%&T}&@}}Fp up`)e!$Cv;Aufb{(J)>1f;Qw!dBizgf7#NoT3r__G29pK`1{NfFYB2%I8xE%c literal 4513 zcmd^@XD}Sz*T+}%wn329e%5Ni>OGA9l|GVeS^X~b~oO|cqd*Be*!+H<7(^wYE2!S?JkDbuZ*4pK=&8kE9CzIzXExM$6q3@NV}r)FQ2a%zGCSw zdsm!ZLHNtXSr2qn;Tn=6@^Wq@AOCY3DKHP|CshE^A^J*xW>bt8C0M!YK)eRK~O#_=C3r(;m;OIIs7Tn$$Vo&Slc^7?etN1 zR8*&qRn++FGxSe6%3e^(9ESk5-Xx2AeOo_`5iK+V%59G>orMlmnS&PO)WYT#^AfRi z^%>S;uH3&%g?NMv-ijSj#jav3l<{@^1JzHzi+OSPJ|2;!)+-u3aTNgWr_20UIDq=@ z=~c6GcIG_Rl5G5=RkqvGP>_4Sc=2IuRv-UWJP@|FO!3L*ACI`yM@R_xXC2o7bj@aV z&G7�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 H{tx*df?EMk From 68053adf7f293ca412312f0e1c2b399597325fc2 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 29 Nov 2017 15:35:16 -0500 Subject: [PATCH 644/721] Quieter snare --- .../scratch3_music/assets/drums/1-snare.mp3 | Bin 5557 -> 5112 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/extensions/scratch3_music/assets/drums/1-snare.mp3 b/src/extensions/scratch3_music/assets/drums/1-snare.mp3 index 38970d352e35a6524e09e0832bb265772eba3542..6cbd0c8c1a961347794a5ca3410b3b4d7a2f4afd 100644 GIT binary patch literal 5112 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 From 6b00f97804d00db2fa9b1fdb98e7577101252cf9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 29 Nov 2017 15:46:04 -0500 Subject: [PATCH 645/721] Fix test --- test/unit/blocks_procedures.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/blocks_procedures.js b/test/unit/blocks_procedures.js index 0285b23d..5dfecea7 100644 --- a/test/unit/blocks_procedures.js +++ b/test/unit/blocks_procedures.js @@ -16,7 +16,7 @@ test('calling a custom block with no definition does not throw', t => { } }; const util = { - getProcedureParamNamesAndIds: () => null, + getProcedureParamNamesAndIds: () => [null, null], stackFrame: { executed: false } From 80da989f0145d1d4ce8ef2cc2addcd40ab7a5035 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 1 Dec 2017 10:29:32 -0500 Subject: [PATCH 646/721] SB2 Import for Broadcast Blocks (not including the input field functionality for 'broadcast' and 'broadcast and wait '). Currently we also cannot import projects where messages and variables share names. --- src/serialization/sb2.js | 59 +++++++++++++++++++++++++++----- src/serialization/sb2_specmap.js | 15 ++++---- test/integration/data.js | 4 +-- test/integration/event.js | 4 +-- test/integration/motion.js | 4 +-- 5 files changed, 62 insertions(+), 24 deletions(-) diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index f6f03bd1..ae501bda 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -88,17 +88,18 @@ const flatten = function (blocks) { * a list of blocks in a branch (e.g., in forever), * or a list of blocks in an argument (e.g., move [pick random...]). * @param {Array.} blockList SB2 JSON-format block list. + * @param {Function} addBroadcastMsg function to update broadcast message name map * @param {Function} getVariableId function to retreive a variable's ID based on name * @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here. * @return {Array.} Scratch VM-format block list. */ -const parseBlockList = function (blockList, getVariableId, extensions) { +const parseBlockList = function (blockList, addBroadcastMsg, getVariableId, extensions) { const resultingList = []; let previousBlock = null; // For setting next. for (let i = 0; i < blockList.length; i++) { const block = blockList[i]; // eslint-disable-next-line no-use-before-define - const parsedBlock = parseBlock(block, getVariableId, extensions); + const parsedBlock = parseBlock(block, addBroadcastMsg, getVariableId, extensions); if (typeof parsedBlock === 'undefined') continue; if (previousBlock) { parsedBlock.parent = previousBlock.id; @@ -115,16 +116,17 @@ const parseBlockList = function (blockList, getVariableId, extensions) { * This should only handle top-level scripts that include X, Y coordinates. * @param {!object} scripts Scripts object from SB2 JSON. * @param {!Blocks} blocks Blocks object to load parsed blocks into. + * @param {Function} addBroadcastMsg function to update broadcast message name map * @param {Function} getVariableId function to retreive a variable's ID based on name * @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here. */ -const parseScripts = function (scripts, blocks, getVariableId, extensions) { +const parseScripts = function (scripts, blocks, addBroadcastMsg, getVariableId, extensions) { for (let i = 0; i < scripts.length; i++) { const script = scripts[i]; const scriptX = script[0]; const scriptY = script[1]; const blockList = script[2]; - const parsedBlockList = parseBlockList(blockList, getVariableId, extensions); + const parsedBlockList = parseBlockList(blockList, addBroadcastMsg, getVariableId, extensions); if (parsedBlockList[0]) { // Adjust script coordinates to account for // larger block size in scratch-blocks. @@ -166,6 +168,23 @@ const generateVariableIdGetter = (function () { }; }()); +const generateBroadcastMsgId = function (name) { + return `broadcastMsgId-${name}`; +}; + +const globalBroadcastMsgStateGenerator = (function () { + let broadcastMsgNameMap = {}; + return function (topLevel) { + if (topLevel) broadcastMsgNameMap = {}; + return { + broadcastMsgMapUpdater: function (name) { + broadcastMsgNameMap[name] = generateBroadcastMsgId(name); + }, + globalBroadcastMsgs: broadcastMsgNameMap + }; + }; +}()); + /** * Parse a single "Scratch object" and create all its in-memory VM objects. * TODO: parse the "info" section, especially "savedExtensions" @@ -227,6 +246,9 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) { const getVariableId = generateVariableIdGetter(target.id, topLevel); + const globalBroadcastMsgObj = globalBroadcastMsgStateGenerator(topLevel); + const addBroadcastMsg = globalBroadcastMsgObj.broadcastMsgMapUpdater; + // Load target properties from JSON. if (object.hasOwnProperty('variables')) { for (let j = 0; j < object.variables.length; j++) { @@ -244,7 +266,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) { // If included, parse any and all scripts/blocks on the object. if (object.hasOwnProperty('scripts')) { - parseScripts(object.scripts, blocks, getVariableId, extensions); + parseScripts(object.scripts, blocks, addBroadcastMsg, getVariableId, extensions); } if (object.hasOwnProperty('lists')) { @@ -317,6 +339,21 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) { Promise.all( childrenPromises ).then(children => { + // Need create broadcast msgs as variables after + // all other targets have finished processing. + if (target.isStage) { + const allBroadcastMsgs = globalBroadcastMsgObj.globalBroadcastMsgs; + for (const msgName in allBroadcastMsgs) { + const msgId = allBroadcastMsgs[msgName]; + const newMsg = new Variable( + msgId, + msgName, + Variable.BROADCAST_MESSAGE_TYPE, + false + ); + target.variables[newMsg.id] = newMsg; + } + } let targets = [target]; for (let n = 0; n < children.length; n++) { targets = targets.concat(children[n]); @@ -349,11 +386,12 @@ const sb2import = function (json, runtime, optForceSprite) { /** * Parse a single SB2 JSON-formatted block and its children. * @param {!object} sb2block SB2 JSON-formatted block. + * @param {Function} addBroadcastMsg function to update broadcast message name map * @param {Function} getVariableId function to retrieve a variable's ID based on name * @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here. * @return {object} Scratch VM format block, or null if unsupported object. */ -const parseBlock = function (sb2block, getVariableId, extensions) { +const parseBlock = function (sb2block, addBroadcastMsg, getVariableId, extensions) { // First item in block object is the old opcode (e.g., 'forward:'). const oldOpcode = sb2block[0]; // Convert the block using the specMap. See sb2specmap.js. @@ -404,10 +442,10 @@ const parseBlock = function (sb2block, getVariableId, extensions) { let innerBlocks; if (typeof providedArg[0] === 'object' && providedArg[0]) { // Block list occupies the input. - innerBlocks = parseBlockList(providedArg, getVariableId, extensions); + innerBlocks = parseBlockList(providedArg, addBroadcastMsg, getVariableId, extensions); } else { // Single block occupies the input. - innerBlocks = [parseBlock(providedArg, getVariableId, extensions)]; + innerBlocks = [parseBlock(providedArg, addBroadcastMsg, getVariableId, extensions)]; } let previousBlock = null; for (let j = 0; j < innerBlocks.length; j++) { @@ -493,6 +531,11 @@ const parseBlock = function (sb2block, getVariableId, extensions) { if (expectedArg.fieldName === 'VARIABLE' || expectedArg.fieldName === 'LIST') { // Add `id` property to variable fields activeBlock.fields[expectedArg.fieldName].id = getVariableId(providedArg); + } else if (expectedArg.fieldName === 'BROADCAST_OPTION') { + // add the name in this field to the broadcast msg name map + addBroadcastMsg(providedArg); + const broadcastId = generateBroadcastMsgId(providedArg); + activeBlock.fields[expectedArg.fieldName].id = broadcastId; } const varType = expectedArg.variableType; if (typeof varType === 'string') { diff --git a/src/serialization/sb2_specmap.js b/src/serialization/sb2_specmap.js index 9e1034bf..cc0a5696 100644 --- a/src/serialization/sb2_specmap.js +++ b/src/serialization/sb2_specmap.js @@ -656,7 +656,8 @@ const specMap = { argMap: [ { type: 'field', - fieldName: 'BROADCAST_OPTION' + fieldName: 'BROADCAST_OPTION', + variableType: Variable.BROADCAST_MESSAGE_TYPE } ] }, @@ -664,9 +665,9 @@ const specMap = { opcode: 'event_broadcast', argMap: [ { - type: 'input', - inputOp: 'event_broadcast_menu', - inputName: 'BROADCAST_OPTION' + type: 'field', + fieldName: 'BROADCAST_OPTION', + variableType: Variable.BROADCAST_MESSAGE_TYPE } ] }, @@ -674,9 +675,9 @@ const specMap = { opcode: 'event_broadcastandwait', argMap: [ { - type: 'input', - inputOp: 'event_broadcast_menu', - inputName: 'BROADCAST_OPTION' + type: 'field', + fieldName: 'BROADCAST_OPTION', + variableType: Variable.BROADCAST_MESSAGE_TYPE } ] }, diff --git a/test/integration/data.js b/test/integration/data.js index 63048fc7..c6cd71d1 100644 --- a/test/integration/data.js +++ b/test/integration/data.js @@ -7,9 +7,7 @@ const VirtualMachine = require('../../src/index'); const uri = path.resolve(__dirname, '../fixtures/data.sb2'); const project = extract(uri); -// TODO (#828) Need to re-activate this test after sb2 import works for broadcast -// blocks -test('data', {todo: true}, t => { +test('data', t => { const vm = new VirtualMachine(); vm.attachStorage(makeTestStorage()); diff --git a/test/integration/event.js b/test/integration/event.js index 648f8cdd..c4f3ee58 100644 --- a/test/integration/event.js +++ b/test/integration/event.js @@ -7,9 +7,7 @@ const VirtualMachine = require('../../src/index'); const uri = path.resolve(__dirname, '../fixtures/event.sb2'); const project = extract(uri); -// TODO (#828) Need to re-activate this test after sb2 import works for broadcast -// blocks -test('event', {todo: true}, t => { +test('event', t => { const vm = new VirtualMachine(); vm.attachStorage(makeTestStorage()); diff --git a/test/integration/motion.js b/test/integration/motion.js index dbb24a11..689b2187 100644 --- a/test/integration/motion.js +++ b/test/integration/motion.js @@ -7,9 +7,7 @@ const VirtualMachine = require('../../src/index'); const uri = path.resolve(__dirname, '../fixtures/motion.sb2'); const project = extract(uri); -// TODO (#828) Need to re-activate this test after sb2 import works for broadcast -// blocks -test('motion', {todo: true}, t => { +test('motion', t => { const vm = new VirtualMachine(); vm.attachStorage(makeTestStorage()); From 9611401e1c34147265bc3d24f271051832aec954 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 1 Dec 2017 10:31:04 -0500 Subject: [PATCH 647/721] Move pen and wedo into extensions folder --- src/extension-support/extension-manager.js | 4 ++-- .../scratch3_pen/index.js} | 16 ++++++++-------- .../scratch3_wedo2/index.js} | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) rename src/{blocks/scratch3_pen.js => extensions/scratch3_pen/index.js} (98%) rename src/{blocks/scratch3_wedo2.js => extensions/scratch3_wedo2/index.js} (99%) diff --git a/src/extension-support/extension-manager.js b/src/extension-support/extension-manager.js index c3346a22..af0dce06 100644 --- a/src/extension-support/extension-manager.js +++ b/src/extension-support/extension-manager.js @@ -6,8 +6,8 @@ const BlockType = require('./block-type'); // These extensions are currently built into the VM repository but should not be loaded at startup. // TODO: move these out into a separate repository? // TODO: change extension spec so that library info, including extension ID, can be collected through static methods -const Scratch3PenBlocks = require('../blocks/scratch3_pen'); -const Scratch3WeDo2Blocks = require('../blocks/scratch3_wedo2'); +const Scratch3PenBlocks = require('../extensions/scratch3_pen'); +const Scratch3WeDo2Blocks = require('../extensions/scratch3_wedo2'); const Scratch3MusicBlocks = require('../extensions/scratch3_music'); const builtinExtensions = { pen: Scratch3PenBlocks, diff --git a/src/blocks/scratch3_pen.js b/src/extensions/scratch3_pen/index.js similarity index 98% rename from src/blocks/scratch3_pen.js rename to src/extensions/scratch3_pen/index.js index 37609952..8b6e51cb 100644 --- a/src/blocks/scratch3_pen.js +++ b/src/extensions/scratch3_pen/index.js @@ -1,11 +1,11 @@ -const ArgumentType = require('../extension-support/argument-type'); -const BlockType = require('../extension-support/block-type'); -const Cast = require('../util/cast'); -const Clone = require('../util/clone'); -const Color = require('../util/color'); -const MathUtil = require('../util/math-util'); -const RenderedTarget = require('../sprites/rendered-target'); -const log = require('../util/log'); +const ArgumentType = require('../../extension-support/argument-type'); +const BlockType = require('../../extension-support/block-type'); +const Cast = require('../../util/cast'); +const Clone = require('../../util/clone'); +const Color = require('../../util/color'); +const MathUtil = require('../../util/math-util'); +const RenderedTarget = require('../../sprites/rendered-target'); +const log = require('../../util/log'); /** * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI. diff --git a/src/blocks/scratch3_wedo2.js b/src/extensions/scratch3_wedo2/index.js similarity index 99% rename from src/blocks/scratch3_wedo2.js rename to src/extensions/scratch3_wedo2/index.js index 1fefe0e1..ad1fd14d 100644 --- a/src/blocks/scratch3_wedo2.js +++ b/src/extensions/scratch3_wedo2/index.js @@ -1,7 +1,7 @@ -const ArgumentType = require('../extension-support/argument-type'); -const BlockType = require('../extension-support/block-type'); -const color = require('../util/color'); -const log = require('../util/log'); +const ArgumentType = require('../../extension-support/argument-type'); +const BlockType = require('../../extension-support/block-type'); +const color = require('../../util/color'); +const log = require('../../util/log'); /** * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI. From ae38ace665a97d72d18f7d27b58be7f0d150dac5 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 1 Dec 2017 10:32:32 -0500 Subject: [PATCH 648/721] Fix path in pen test --- test/integration/pen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/pen.js b/test/integration/pen.js index 7df798e7..bb9f7c82 100644 --- a/test/integration/pen.js +++ b/test/integration/pen.js @@ -2,7 +2,7 @@ const Worker = require('tiny-worker'); const path = require('path'); const test = require('tap').test; -const Scratch3PenBlocks = require('../../src/blocks/scratch3_pen'); +const Scratch3PenBlocks = require('../../src/extensions/scratch3_pen/index.js'); const VirtualMachine = require('../../src/index'); const dispatch = require('../../src/dispatch/central-dispatch'); From 2091ef06362a4bd443d7661e0294eb626c60931e Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 1 Dec 2017 11:06:46 -0500 Subject: [PATCH 649/721] Add icon for music extension blocks --- src/extensions/scratch3_music/index.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index 807c3f79..6d661bd2 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -5,6 +5,13 @@ const Cast = require('../../util/cast'); const MathUtil = require('../../util/math-util'); const Timer = require('../../util/timer'); +/** + * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI. + * @type {string} + */ +// eslint-disable-next-line max-len +const iconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PHRpdGxlPm11c2ljLWJsb2NrLWljb248L3RpdGxlPjxkZWZzPjxwYXRoIGQ9Ik0zMi4xOCAyNS44NzRDMzIuNjM2IDI4LjE1NyAzMC41MTIgMzAgMjcuNDMzIDMwYy0zLjA3IDAtNS45MjMtMS44NDMtNi4zNzItNC4xMjYtLjQ1OC0yLjI4NSAxLjY2NS00LjEzNiA0Ljc0My00LjEzNi42NDcgMCAxLjI4My4wODQgMS44OS4yMzQuMzM4LjA4Ni42MzcuMTguOTM4LjMwMi44Ny0uMDItLjEwNC0yLjI5NC0xLjgzNS0xMi4yMy0yLjEzNC0xMi4zMDIgMy4wNi0xLjg3IDguNzY4LTIuNzUyIDUuNzA4LS44ODUuMDc2IDQuODItMy42NSAzLjg0NC0zLjcyNC0uOTg3LTQuNjUtNy4xNTMuMjYzIDE0LjczOHptLTE2Ljk5OCA1Ljk5QzE1LjYzIDM0LjE0OCAxMy41MDcgMzYgMTAuNDQgMzZjLTMuMDcgMC01LjkyMi0xLjg1Mi02LjM4LTQuMTM2LS40NDgtMi4yODQgMS42NzQtNC4xMzUgNC43NS00LjEzNSAxLjAwMyAwIDEuOTc1LjE5NiAyLjg1NS41NDMuODIyLS4wNTUtLjE1LTIuMzc3LTEuODYyLTEyLjIyOC0yLjEzMy0xMi4zMDMgMy4wNi0xLjg3IDguNzY0LTIuNzUzIDUuNzA2LS44OTQuMDc2IDQuODItMy42NDggMy44MzQtMy43MjQtLjk4Ny00LjY1LTcuMTUyLjI2MiAxNC43Mzh6IiBpZD0iYSIvPjwvZGVmcz48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjx1c2UgZmlsbD0iI0ZGRiIgeGxpbms6aHJlZj0iI2EiLz48cGF0aCBzdHJva2Utb3BhY2l0eT0iLjEiIHN0cm9rZT0iIzAwMCIgZD0iTTI4LjQ1NiAyMS42NzVjLS4wMS0uMzEyLS4wODctLjgyNS0uMjU2LTEuNzAyLS4wOTYtLjQ5NS0uNjEyLTMuMDIyLS43NTMtMy43My0uMzk1LTEuOTgtLjc2LTMuOTItMS4xNDItNi4xMTMtLjczMi00LjIyMy0uNjkzLTYuMDUuMzQ0LTYuNTI3LjUtLjIzIDEuMDYtLjA4IDEuODQuMzUuNDE0LjIyNyAyLjE4MiAxLjM2NSAyLjA3IDEuMjk2IDEuOTk0IDEuMjQyIDMuNDY0IDEuNzc0IDQuOTMgMS41NDggMS41MjYtLjIzNyAyLjUwNC0uMDYgMi44NzYuNjE4LjM0OC42MzUuMDE1IDEuNDE2LS43MyAyLjE4LTEuNDcyIDEuNTE2LTMuOTc1IDIuNTE0LTUuODQ4IDIuMDIzLS44MjItLjIyLTEuMjM4LS40NjUtMi4zOC0xLjI2N2wtLjA5NS0uMDY2Yy4wNDcuNTkzLjI2NCAxLjc0LjcxNyAzLjgwMy4yOTQgMS4zMzYgMi4wOCA5LjE4NyAyLjYzNyAxMS42NzRsLjAwMi4wMTJjLjUyOCAyLjYzNy0xLjg3MyA0LjcyNC01LjIzNiA0LjcyNC0zLjI5IDAtNi4zNjMtMS45ODgtNi44NjItNC41MjgtLjUzLTIuNjQgMS44NzMtNC43MzQgNS4yMzMtNC43MzQuNjcyIDAgMS4zNDcuMDg1IDIuMDE0LjI1LjIyNy4wNTcuNDM2LjExOC42MzYuMTg3em0tMTYuOTk2IDUuOTljLS4wMS0uMzE4LS4wOS0uODM4LS4yNjYtMS43MzctLjA5LS40Ni0uNTk1LTIuOTM3LS43NTMtMy43MjctLjM5LTEuOTYtLjc1LTMuODktMS4xMy02LjA3LS43MzItNC4yMjMtLjY5Mi02LjA1LjM0NC02LjUyNi41MDItLjIzIDEuMDYtLjA4MiAxLjg0LjM1LjQxNS4yMjcgMi4xODIgMS4zNjQgMi4wNyAxLjI5NSAxLjk5MyAxLjI0MiAzLjQ2MiAxLjc3NCA0LjkyNiAxLjU0OCAxLjUyNS0uMjQgMi41MDQtLjA2NCAyLjg3Ni42MTQuMzQ4LjYzNS4wMTUgMS40MTUtLjcyOCAyLjE4LTEuNDc0IDEuNTE3LTMuOTc3IDIuNTEzLTUuODQ3IDIuMDE3LS44Mi0uMjItMS4yMzYtLjQ2NC0yLjM3OC0xLjI2N2wtLjA5NS0uMDY1Yy4wNDcuNTkzLjI2NCAxLjc0LjcxNyAzLjgwMi4yOTQgMS4zMzcgMi4wNzggOS4xOSAyLjYzNiAxMS42NzVsLjAwMy4wMTNjLjUxNyAyLjYzOC0xLjg4NCA0LjczMi01LjIzNCA0LjczMi0zLjI4NyAwLTYuMzYtMS45OTMtNi44Ny00LjU0LS41Mi0yLjY0IDEuODg0LTQuNzMgNS4yNC00LjczLjkwNSAwIDEuODAzLjE1IDIuNjUuNDM2eiIvPjwvZz48L3N2Zz4='; + /** * Class for the music-related blocks in Scratch 3.0 * @param {Runtime} runtime - the runtime instantiating this block package. @@ -395,6 +402,7 @@ class Scratch3MusicBlocks { return { id: 'music', name: 'Music', + iconURI: iconURI, blocks: [ { opcode: 'playDrumForBeats', From aab4a45622c5448331306ea0f7b2304721d8e37d Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 1 Dec 2017 11:27:54 -0500 Subject: [PATCH 650/721] Minor Refactoring/cleanup. --- src/serialization/sb2.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index ae501bda..d19175dc 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -168,17 +168,14 @@ const generateVariableIdGetter = (function () { }; }()); -const generateBroadcastMsgId = function (name) { - return `broadcastMsgId-${name}`; -}; - const globalBroadcastMsgStateGenerator = (function () { let broadcastMsgNameMap = {}; return function (topLevel) { if (topLevel) broadcastMsgNameMap = {}; return { broadcastMsgMapUpdater: function (name) { - broadcastMsgNameMap[name] = generateBroadcastMsgId(name); + broadcastMsgNameMap[name] = `broadcastMsgId-${name}`; + return broadcastMsgNameMap[name]; }, globalBroadcastMsgs: broadcastMsgNameMap }; @@ -533,8 +530,7 @@ const parseBlock = function (sb2block, addBroadcastMsg, getVariableId, extension activeBlock.fields[expectedArg.fieldName].id = getVariableId(providedArg); } else if (expectedArg.fieldName === 'BROADCAST_OPTION') { // add the name in this field to the broadcast msg name map - addBroadcastMsg(providedArg); - const broadcastId = generateBroadcastMsgId(providedArg); + const broadcastId = addBroadcastMsg(providedArg); activeBlock.fields[expectedArg.fieldName].id = broadcastId; } const varType = expectedArg.variableType; From eede8105d1ecca28464f26e8d1436069376c22ad Mon Sep 17 00:00:00 2001 From: sjhuang26 Date: Fri, 1 Dec 2017 20:06:55 -0500 Subject: [PATCH 651/721] Clear answer on green flag --- src/blocks/scratch3_sensing.js | 5 +++++ src/engine/runtime.js | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index 19357c88..b23fe740 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -21,6 +21,7 @@ class Scratch3SensingBlocks { this._questionList = []; this.runtime.on('ANSWER', this._onAnswer.bind(this)); + this.runtime.on('PROJECT_START', this._resetAnswer.bind(this)); this.runtime.on('PROJECT_STOP_ALL', this._clearAllQuestions.bind(this)); } @@ -73,6 +74,10 @@ class Scratch3SensingBlocks { } } + _resetAnswer () { + this._answer = ''; + } + _enqueueAsk (question, resolve, target, wasVisible, wasStage) { this._questionList.push([question, resolve, target, wasVisible, wasStage]); } diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 8131bdd8..a8e044e3 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -317,6 +317,15 @@ class Runtime extends EventEmitter { return 'BLOCK_GLOW_OFF'; } + /** + * Event name when the project is started (threads may not necessarily be + * running). + * @const {string} + */ + static get PROJECT_START () { + return 'PROJECT_START'; + } + /** * Event name when threads start running. * Used by the UI to indicate running status. @@ -1057,6 +1066,7 @@ class Runtime extends EventEmitter { */ greenFlag () { this.stopAll(); + this.emit(Runtime.PROJECT_START); this.ioDevices.clock.resetProjectTimer(); this.clearEdgeActivatedValues(); // Inform all targets of the green flag. From b2802b75f12660f37b9bb39fb66c9860d78f6e4e Mon Sep 17 00:00:00 2001 From: sjhuang26 Date: Sat, 2 Dec 2017 08:33:32 -0500 Subject: [PATCH 652/721] Go to front in stage does nothing --- src/blocks/scratch3_looks.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index 6c8f4fe3..1429b6dd 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -410,7 +410,9 @@ class Scratch3LooksBlocks { } goToFront (args, util) { - util.target.goToFront(); + if (!util.target.isStage) { + util.target.goToFront(); + } } goBackLayers (args, util) { From 48505fd7e204fcaea2d8893c1b59fec0295a513c Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Mon, 4 Dec 2017 18:48:49 +0000 Subject: [PATCH 653/721] chore(package): update webpack to version 3.10.0 Closes #620 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2ccc8089..0830dee9 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "stats.js": "^0.17.0", "tap": "^10.2.0", "tiny-worker": "^2.1.1", - "webpack": "^2.4.1", + "webpack": "^3.10.0", "webpack-dev-server": "^2.4.1", "worker-loader": "1.1.0" } From 34b0aff6370bf54bbefa24e25745a633796bb0ba Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 4 Dec 2017 18:01:29 -0500 Subject: [PATCH 654/721] Bugfix for scratch-gui issue #994, where executing a broadcast block from the flyout was creating a conflicting variable, causing a fatal error. --- src/blocks/scratch3_event.js | 50 +++++++++++++++++++----------------- src/engine/target.js | 23 +++++++++++------ test/unit/blocks_event.js | 6 +++-- test/unit/engine_target.js | 17 ++---------- 4 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/blocks/scratch3_event.js b/src/blocks/scratch3_event.js index 5fbaa5b7..5ee2fab1 100644 --- a/src/blocks/scratch3_event.js +++ b/src/blocks/scratch3_event.js @@ -56,36 +56,40 @@ class Scratch3EventBlocks { } broadcast (args, util) { - const broadcastVar = util.runtime.getTargetForStage().lookupOrCreateBroadcastMsg( + const broadcastVar = util.runtime.getTargetForStage().lookupBroadcastMsg( args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name); - const broadcastOption = broadcastVar.name; - util.startHats('event_whenbroadcastreceived', { - BROADCAST_OPTION: broadcastOption - }); + if (broadcastVar) { + const broadcastOption = broadcastVar.name; + util.startHats('event_whenbroadcastreceived', { + BROADCAST_OPTION: broadcastOption + }); + } } broadcastAndWait (args, util) { - const broadcastVar = util.runtime.getTargetForStage().lookupOrCreateBroadcastMsg( + const broadcastVar = util.runtime.getTargetForStage().lookupBroadcastMsg( args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name); - const broadcastOption = broadcastVar.name; - // Have we run before, starting threads? - if (!util.stackFrame.startedThreads) { - // No - start hats for this broadcast. - util.stackFrame.startedThreads = util.startHats( - 'event_whenbroadcastreceived', { - BROADCAST_OPTION: broadcastOption + if (broadcastVar) { + const broadcastOption = broadcastVar.name; + // Have we run before, starting threads? + if (!util.stackFrame.startedThreads) { + // No - start hats for this broadcast. + util.stackFrame.startedThreads = util.startHats( + 'event_whenbroadcastreceived', { + BROADCAST_OPTION: broadcastOption + } + ); + if (util.stackFrame.startedThreads.length === 0) { + // Nothing was started. + return; } - ); - if (util.stackFrame.startedThreads.length === 0) { - // Nothing was started. - return; } - } - // We've run before; check if the wait is still going on. - const instance = this; - const waiting = util.stackFrame.startedThreads.some(thread => instance.runtime.isActiveThread(thread)); - if (waiting) { - util.yield(); + // We've run before; check if the wait is still going on. + const instance = this; + const waiting = util.stackFrame.startedThreads.some(thread => instance.runtime.isActiveThread(thread)); + if (waiting) { + util.yield(); + } } } } diff --git a/src/engine/target.js b/src/engine/target.js index 39273fcc..4e574177 100644 --- a/src/engine/target.js +++ b/src/engine/target.js @@ -4,6 +4,7 @@ const Blocks = require('./blocks'); const Variable = require('../engine/variable'); const uid = require('../util/uid'); const {Map} = require('immutable'); +const log = require('../util/log'); /** * @fileoverview @@ -93,19 +94,25 @@ class Target extends EventEmitter { } /** - * Look up a broadcast message object, and create it if one doesn't exist. + * Look up a broadcast message object with the given id and return it + * if it exists. * @param {string} id Id of the variable. * @param {string} name Name of the variable. * @return {!Variable} Variable object. */ - lookupOrCreateBroadcastMsg (id, name) { + lookupBroadcastMsg (id, name) { const broadcastMsg = this.lookupVariableById(id); - if (broadcastMsg) return broadcastMsg; - // No variable with this name exists - create it locally. - const newBroadcastMsg = new Variable(id, name, - Variable.BROADCAST_MESSAGE_TYPE, false); - this.variables[id] = newBroadcastMsg; - return newBroadcastMsg; + if (broadcastMsg) { + if (broadcastMsg.name !== name) { + log.error(`Found broadcast message with id: ${id}, but` + + `its name, ${broadcastMsg.name} did not match expected name ${name}.`); + } + if (broadcastMsg.type !== Variable.BROADCAST_MESSAGE_TYPE) { + log.error(`Found variable with id: ${id}, but its type ${broadcastMsg.type}` + + `did not match expected type ${Variable.BROADCAST_MESSAGE_TYPE}`); + } + return broadcastMsg; + } } /** diff --git a/test/unit/blocks_event.js b/test/unit/blocks_event.js index 35a1f0b4..672ea332 100644 --- a/test/unit/blocks_event.js +++ b/test/unit/blocks_event.js @@ -6,6 +6,7 @@ const Event = require('../../src/blocks/scratch3_event'); const Runtime = require('../../src/engine/runtime'); const Target = require('../../src/engine/target'); const Thread = require('../../src/engine/thread'); +const Variable = require('../../src/engine/variable'); test('#760 - broadcastAndWait', t => { const broadcastAndWaitBlock = { @@ -52,6 +53,7 @@ test('#760 - broadcastAndWait', t => { b.createBlock(receiveMessageBlock); const tgt = new Target(rt, b); tgt.isStage = true; + tgt.createVariable('testBroadcastID', 'message', Variable.BROADCAST_MESSAGE_TYPE); rt.targets.push(tgt); let th = rt._pushThread('broadcastAndWaitBlock', t); @@ -67,12 +69,12 @@ test('#760 - broadcastAndWait', t => { // yields when some thread is active t.strictEqual(th.status, Thread.STATUS_YIELD); th.status = Thread.STATUS_RUNNING; - e.broadcastAndWait({BROADCAST_OPTION: 'message'}, util); + e.broadcastAndWait({BROADCAST_OPTION: {id: 'testBroadcastID', name: 'message'}}, util); t.strictEqual(th.status, Thread.STATUS_YIELD); // does not yield once all threads are done th.status = Thread.STATUS_RUNNING; rt.threads[1].status = Thread.STATUS_DONE; - e.broadcastAndWait({BROADCAST_OPTION: 'message'}, util); + e.broadcastAndWait({BROADCAST_OPTION: {id: 'testBroadcastID', name: 'message'}}, util); t.strictEqual(th.status, Thread.STATUS_RUNNING); // restarts done threads that are in runtime threads diff --git a/test/unit/engine_target.js b/test/unit/engine_target.js index 4f663906..30ff5eb8 100644 --- a/test/unit/engine_target.js +++ b/test/unit/engine_target.js @@ -174,20 +174,7 @@ test('lookupOrCreateList returns list if one with given id exists', t => { t.end(); }); -test('lookupOrCreateBroadcastMsg creates a var if one does not exist', t => { - const target = new Target(); - const variables = target.variables; - - t.equal(Object.keys(variables).length, 0); - const broadcastVar = target.lookupOrCreateBroadcastMsg('foo', 'bar'); - t.equal(Object.keys(variables).length, 1); - t.equal(broadcastVar.id, 'foo'); - t.equal(broadcastVar.name, 'bar'); - - t.end(); -}); - -test('lookupOrCreateBroadcastMsg returns the var with given id if exists', t => { +test('lookupBroadcastMsg returns the var with given id if exists', t => { const target = new Target(); const variables = target.variables; @@ -195,7 +182,7 @@ test('lookupOrCreateBroadcastMsg returns the var with given id if exists', t => target.createVariable('foo', 'bar', Variable.BROADCAST_MESSAGE_TYPE); t.equal(Object.keys(variables).length, 1); - const broadcastMsg = target.lookupOrCreateBroadcastMsg('foo', 'bar'); + const broadcastMsg = target.lookupBroadcastMsg('foo', 'bar'); t.equal(Object.keys(variables).length, 1); t.equal(broadcastMsg.id, 'foo'); t.equal(broadcastMsg.name, 'bar'); From 2d2b691b6f0e5c9f082c3f85639185c4fd987784 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 6 Dec 2017 11:21:00 -0500 Subject: [PATCH 655/721] Return a default falsey value for unknown params based on param type --- src/blocks/scratch3_procedures.js | 17 ++++++++++++++--- test/unit/engine_thread.js | 4 +++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/blocks/scratch3_procedures.js b/src/blocks/scratch3_procedures.js index 645e582e..3ba743d8 100644 --- a/src/blocks/scratch3_procedures.js +++ b/src/blocks/scratch3_procedures.js @@ -15,8 +15,8 @@ class Scratch3ProcedureBlocks { return { procedures_definition: this.definition, procedures_call: this.call, - argument_reporter_string_number: this.param, - argument_reporter_boolean: this.param + argument_reporter_string_number: this.argumentReporterStringNumber, + argument_reporter_boolean: this.argumentReporterBoolean }; } @@ -47,8 +47,19 @@ class Scratch3ProcedureBlocks { } } - param (args, util) { + argumentReporterStringNumber (args, util) { const value = util.getParam(args.VALUE); + if (value === null) { + return ''; + } + return value; + } + + argumentReporterBoolean (args, util) { + const value = util.getParam(args.VALUE); + if (value === null) { + return false; + } return value; } } diff --git a/test/unit/engine_thread.js b/test/unit/engine_thread.js index 533f7252..15fab7c0 100644 --- a/test/unit/engine_thread.js +++ b/test/unit/engine_thread.js @@ -110,7 +110,9 @@ test('PushGetParam', t => { th.pushParam('testParam', 'testValue'); t.strictEquals(th.peekStackFrame().params.testParam, 'testValue'); t.strictEquals(th.getParam('testParam'), 'testValue'); - + // Params outside of define stack always evaluate to null + t.strictEquals(th.getParam('nonExistentParam'), null); + t.end(); }); From f51cf9877eac972f733905d4de5b0f6b7aabf8af Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Mon, 11 Dec 2017 15:41:45 -0500 Subject: [PATCH 656/721] Preliminary localization (#777) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * localize the block and menu strings in the pen extension * adds .tx/config to be able to push translations to transifex * includes format-message to localize strings and extracting them. * add setLocale function to VM to allow GUI to pass in locale data. * refresh block definitions when the locale changes. ### Still to be decided For now just extracting messages from the pen extension into their own file. We’ll need to decide if each category gets its own file, or group all the strings into one resource. --- .gitignore | 3 + .tx/config | 8 ++ package.json | 6 +- src/engine/runtime.js | 43 +++++++ src/extension-support/extension-manager.js | 26 ++++- src/extensions/scratch3_pen/index.js | 130 ++++++++++++++++++--- src/virtual-machine.js | 16 +++ 7 files changed, 210 insertions(+), 22 deletions(-) create mode 100644 .tx/config diff --git a/.gitignore b/.gitignore index a733474c..015fdc57 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ npm-* # Build /dist /playground + +# Localization +/translations diff --git a/.tx/config b/.tx/config new file mode 100644 index 00000000..b6cb702a --- /dev/null +++ b/.tx/config @@ -0,0 +1,8 @@ +[main] +host = https://www.transifex.com + +[experimental-scratch.pen] +file_filter = translations/pen/.json +source_file = translations/pen/en.json +source_lang = en +type = CHROME diff --git a/package.json b/package.json index aa5de51a..d6902c5d 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,9 @@ "build": "webpack --progress --colors --bail", "coverage": "tap ./test/{unit,integration}/*.js --coverage --coverage-report=lcov", "deploy": "touch playground/.nojekyll && gh-pages -t -d playground -m \"Build for $(git log --pretty=format:%H -n1)\"", - "lint": "eslint .", + "extract:pen": "mkdirp translations/pen && format-message extract --out-file translations/pen/en.json src/extensions/scratch3_pen/index.js", + "i18n:src": "npm run extract:pen", + "lint": "eslint . && format-message lint src/**/*.js", "prepublish": "in-publish && npm run build || not-in-publish", "start": "webpack-dev-server", "tap": "tap ./test/{unit,integration}/*.js", @@ -37,6 +39,8 @@ "eslint": "^4.5.0", "eslint-config-scratch": "^5.0.0", "expose-loader": "0.7.4", + "format-message": "5.2.1", + "format-message-cli": "5.2.1", "gh-pages": "^1.1.0", "highlightjs": "^9.8.0", "htmlparser2": "3.9.2", diff --git a/src/engine/runtime.js b/src/engine/runtime.js index d0c09cc7..7ab5aa2b 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -385,6 +385,14 @@ class Runtime extends EventEmitter { return 'EXTENSION_ADDED'; } + /** + * Event name for reporting that blocksInfo was updated. + * @const {string} + */ + static get BLOCKSINFO_UPDATE () { + return 'BLOCKSINFO_UPDATE'; + } + /** * How rapidly we try to step threads by default, in ms. */ @@ -497,6 +505,41 @@ class Runtime extends EventEmitter { this.emit(Runtime.EXTENSION_ADDED, categoryInfo.blocks.concat(categoryInfo.menus)); } + /** + * Reregister the primitives for an extension + * @param {ExtensionInfo} extensionInfo - new info (results of running getInfo) + * for an extension + * @private + */ + _refreshExtensionPrimitives (extensionInfo) { + let extensionBlocks = []; + for (const categoryInfo of this._blockInfo) { + if (extensionInfo.id === categoryInfo.id) { + categoryInfo.blocks = []; + categoryInfo.menus = []; + for (const menuName in extensionInfo.menus) { + if (extensionInfo.menus.hasOwnProperty(menuName)) { + const menuItems = extensionInfo.menus[menuName]; + const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuItems, categoryInfo); + categoryInfo.menus.push(convertedMenu); + } + } + for (const blockInfo of extensionInfo.blocks) { + const convertedBlock = this._convertForScratchBlocks(blockInfo, categoryInfo); + const opcode = convertedBlock.json.type; + categoryInfo.blocks.push(convertedBlock); + this._primitives[opcode] = convertedBlock.info.func; + if (blockInfo.blockType === BlockType.HAT) { + this._hats[opcode] = {edgeActivated: true}; /** @TODO let extension specify this */ + } + } + extensionBlocks = extensionBlocks.concat(categoryInfo.blocks, categoryInfo.menus); + } + } + + this.emit(Runtime.BLOCKSINFO_UPDATE, extensionBlocks); + } + /** * Build the scratch-blocks JSON for a menu. Note that scratch-blocks treats menus as a special kind of block. * @param {string} menuName - the name of the menu diff --git a/src/extension-support/extension-manager.js b/src/extension-support/extension-manager.js index af0dce06..3228f5c2 100644 --- a/src/extension-support/extension-manager.js +++ b/src/extension-support/extension-manager.js @@ -78,7 +78,7 @@ class ExtensionManager { * @type {Set.} * @private */ - this._loadedExtensions = new Set(); + this._loadedExtensions = new Map(); /** * Keep a reference to the runtime so we can construct internal extension objects. @@ -119,8 +119,8 @@ class ExtensionManager { const extension = builtinExtensions[extensionURL]; const extensionInstance = new extension(this.runtime); - return this._registerInternalExtension(extensionInstance).then(() => { - this._loadedExtensions.add(extensionURL); + return this._registerInternalExtension(extensionInstance).then(serviceName => { + this._loadedExtensions.set(extensionURL, serviceName); }); } @@ -133,6 +133,21 @@ class ExtensionManager { }); } + /** + * regenerate blockinfo for any loaded extensions + */ + refreshBlocks () { + this._loadedExtensions.forEach(serviceName => { + dispatch.call(serviceName, 'getInfo') + .then(info => { + dispatch.call('runtime', '_refreshExtensionPrimitives', info); + }) + .catch(e => { + log.error(`Failed to refresh buildtin extension primitives: ${JSON.stringify(e)}`); + }); + }); + } + allocateWorker () { const id = this.nextExtensionWorker++; const workerInfo = this.pendingExtensions.shift(); @@ -175,7 +190,10 @@ class ExtensionManager { const fakeWorkerId = this.nextExtensionWorker++; const serviceName = `extension.${fakeWorkerId}.${extensionInfo.id}`; return dispatch.setService(serviceName, extensionObject) - .then(() => dispatch.call('extensions', 'registerExtensionService', serviceName)); + .then(() => { + dispatch.call('extensions', 'registerExtensionService', serviceName); + return serviceName; + }); } /** diff --git a/src/extensions/scratch3_pen/index.js b/src/extensions/scratch3_pen/index.js index 8b6e51cb..e194a1d5 100644 --- a/src/extensions/scratch3_pen/index.js +++ b/src/extensions/scratch3_pen/index.js @@ -3,6 +3,7 @@ const BlockType = require('../../extension-support/block-type'); const Cast = require('../../util/cast'); const Clone = require('../../util/clone'); const Color = require('../../util/color'); +const formatMessage = require('format-message'); const MathUtil = require('../../util/math-util'); const RenderedTarget = require('../../sprites/rendered-target'); const log = require('../../util/log'); @@ -15,7 +16,7 @@ const log = require('../../util/log'); const iconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+cGVuLWljb248L3RpdGxlPjxnIHN0cm9rZT0iIzU3NUU3NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik04Ljc1MyAzNC42MDJsLTQuMjUgMS43OCAxLjc4My00LjIzN2MxLjIxOC0yLjg5MiAyLjkwNy01LjQyMyA1LjAzLTcuNTM4TDMxLjA2NiA0LjkzYy44NDYtLjg0MiAyLjY1LS40MSA0LjAzMi45NjcgMS4zOCAxLjM3NSAxLjgxNiAzLjE3My45NyA0LjAxNUwxNi4zMTggMjkuNTljLTIuMTIzIDIuMTE2LTQuNjY0IDMuOC03LjU2NSA1LjAxMiIgZmlsbD0iI0ZGRiIvPjxwYXRoIGQ9Ik0yOS40MSA2LjExcy00LjQ1LTIuMzc4LTguMjAyIDUuNzcyYy0xLjczNCAzLjc2Ni00LjM1IDEuNTQ2LTQuMzUgMS41NDYiLz48cGF0aCBkPSJNMzYuNDIgOC44MjVjMCAuNDYzLS4xNC44NzMtLjQzMiAxLjE2NGwtOS4zMzUgOS4zYy4yODItLjI5LjQxLS42NjguNDEtMS4xMiAwLS44NzQtLjUwNy0xLjk2My0xLjQwNi0yLjg2OC0xLjM2Mi0xLjM1OC0zLjE0Ny0xLjgtNC4wMDItLjk5TDMwLjk5IDUuMDFjLjg0NC0uODQgMi42NS0uNDEgNC4wMzUuOTYuODk4LjkwNCAxLjM5NiAxLjk4MiAxLjM5NiAyLjg1NU0xMC41MTUgMzMuNzc0Yy0uNTczLjMwMi0xLjE1Ny41Ny0xLjc2NC44M0w0LjUgMzYuMzgybDEuNzg2LTQuMjM1Yy4yNTgtLjYwNC41My0xLjE4Ni44MzMtMS43NTcuNjkuMTgzIDEuNDQ4LjYyNSAyLjEwOCAxLjI4Mi42Ni42NTggMS4xMDIgMS40MTIgMS4yODcgMi4xMDIiIGZpbGw9IiM0Qzk3RkYiLz48cGF0aCBkPSJNMzYuNDk4IDguNzQ4YzAgLjQ2NC0uMTQuODc0LS40MzMgMS4xNjVsLTE5Ljc0MiAxOS42OGMtMi4xMyAyLjExLTQuNjczIDMuNzkzLTcuNTcyIDUuMDFMNC41IDM2LjM4bC45NzQtMi4zMTYgMS45MjUtLjgwOGMyLjg5OC0xLjIxOCA1LjQ0LTIuOSA3LjU3LTUuMDFsMTkuNzQzLTE5LjY4Yy4yOTItLjI5Mi40MzItLjcwMi40MzItMS4xNjUgMC0uNjQ2LS4yNy0xLjQtLjc4LTIuMTIyLjI1LjE3Mi41LjM3Ny43MzcuNjE0Ljg5OC45MDUgMS4zOTYgMS45ODMgMS4zOTYgMi44NTYiIGZpbGw9IiM1NzVFNzUiIG9wYWNpdHk9Ii4xNSIvPjxwYXRoIGQ9Ik0xOC40NSAxMi44M2MwIC41LS40MDQuOTA1LS45MDQuOTA1cy0uOTA1LS40MDUtLjkwNS0uOTA0YzAtLjUuNDA3LS45MDMuOTA2LS45MDMuNSAwIC45MDQuNDA0LjkwNC45MDR6IiBmaWxsPSIjNTc1RTc1Ii8+PC9nPjwvc3ZnPg=='; /** - * Enum for pen color parameters. + * Enum for pen color parameter values. * @readonly * @enum {string} */ @@ -201,6 +202,49 @@ class Scratch3PenBlocks { return MathUtil.wrapClamp(value, 0, 100); } + /** + * Initialize color parameters menu with localized strings + * @returns {array} of the localized text and values for each menu element + * @private + */ + _initColorParam () { + return [ + { + text: formatMessage({ + id: 'pen.colorMenu.color', + default: 'color', + description: 'label for color element in color picker for pen extension' + }), + value: ColorParam.COLOR + }, + { + text: formatMessage({ + id: 'pen.colorMenu.saturation', + default: 'saturation', + description: 'label for saturation element in color picker for pen extension' + }), + value: ColorParam.SATURATION + }, + { + text: formatMessage({ + id: 'pen.colorMenu.brightness', + default: 'brightness', + description: 'label for brightness element in color picker for pen extension' + }), + value: ColorParam.BRIGHTNESS + }, + { + text: formatMessage({ + id: 'pen.colorMenu.transparency', + default: 'transparency', + description: 'label for transparency element in color picker for pen extension' + }), + value: ColorParam.TRANSPARENCY + + } + ]; + } + /** * Clamp a pen color parameter to the range (0,100). * @param {number} value - the value to be clamped. @@ -246,26 +290,48 @@ class Scratch3PenBlocks { blocks: [ { opcode: 'clear', - blockType: BlockType.COMMAND + blockType: BlockType.COMMAND, + text: formatMessage({ + id: 'pen.clear', + default: 'clear', + description: 'erase all pen trails and stamps' + }) }, { opcode: 'stamp', - blockType: BlockType.COMMAND + blockType: BlockType.COMMAND, + text: formatMessage({ + id: 'pen.stamp', + default: 'stamp', + description: 'render current costume on the background' + }) }, { opcode: 'penDown', blockType: BlockType.COMMAND, - text: 'pen down' + text: formatMessage({ + id: 'pen.penDown', + default: 'pen down', + description: 'start leaving a trail when the sprite moves' + }) }, { opcode: 'penUp', blockType: BlockType.COMMAND, - text: 'pen up' + text: formatMessage({ + id: 'pen.penUp', + default: 'pen up', + description: 'stop leaving a trail behind the sprite' + }) }, { opcode: 'setPenColorToColor', blockType: BlockType.COMMAND, - text: 'set pen color to [COLOR]', + text: formatMessage({ + id: 'pen.setColor', + default: 'set pen color to [COLOR]', + description: 'set the pen color to a particular (RGB) value' + }), arguments: { COLOR: { type: ArgumentType.COLOR @@ -275,7 +341,11 @@ class Scratch3PenBlocks { { opcode: 'changePenColorParamBy', blockType: BlockType.COMMAND, - text: 'change pen [COLOR_PARAM] by [VALUE]', + text: formatMessage({ + id: 'pen.changeColorParam', + default: 'change pen [COLOR_PARAM] by [VALUE]', + description: 'change the state of a pen color parameter' + }), arguments: { COLOR_PARAM: { type: ArgumentType.STRING, @@ -291,7 +361,11 @@ class Scratch3PenBlocks { { opcode: 'setPenColorParamTo', blockType: BlockType.COMMAND, - text: 'set pen [COLOR_PARAM] to [VALUE]', + text: formatMessage({ + id: 'pen.setColorParam', + default: 'set pen [COLOR_PARAM] to [VALUE]', + description: 'set the state for a pen color parameter e.g. saturation' + }), arguments: { COLOR_PARAM: { type: ArgumentType.STRING, @@ -307,7 +381,11 @@ class Scratch3PenBlocks { { opcode: 'changePenSizeBy', blockType: BlockType.COMMAND, - text: 'change pen size by [SIZE]', + text: formatMessage({ + id: 'pen.changeSize', + default: 'change pen size by [SIZE]', + description: 'change the diameter of the trail left by a sprite' + }), arguments: { SIZE: { type: ArgumentType.NUMBER, @@ -318,7 +396,11 @@ class Scratch3PenBlocks { { opcode: 'setPenSizeTo', blockType: BlockType.COMMAND, - text: 'set pen size to [SIZE]', + text: formatMessage({ + id: 'pen.setSize', + default: 'set pen size to [SIZE]', + description: 'set the diameter of a trail left by a sprite' + }), arguments: { SIZE: { type: ArgumentType.NUMBER, @@ -330,7 +412,11 @@ class Scratch3PenBlocks { { opcode: 'setPenShadeToNumber', blockType: BlockType.COMMAND, - text: 'set pen shade to [SHADE]', + text: formatMessage({ + id: 'pen.setShade', + default: 'set pen shade to [SHADE]', + description: 'legacy pen blocks - set pen shade' + }), arguments: { SHADE: { type: ArgumentType.NUMBER, @@ -342,7 +428,11 @@ class Scratch3PenBlocks { { opcode: 'changePenShadeBy', blockType: BlockType.COMMAND, - text: 'change pen shade by [SHADE]', + text: formatMessage({ + id: 'pen.changeShade', + default: 'change pen shade by [SHADE]', + description: 'legacy pen blocks - change pen shade' + }), arguments: { SHADE: { type: ArgumentType.NUMBER, @@ -354,7 +444,11 @@ class Scratch3PenBlocks { { opcode: 'setPenHueToNumber', blockType: BlockType.COMMAND, - text: 'set pen hue to [HUE]', + text: formatMessage({ + id: 'pen.setHue', + default: 'set pen hue to [HUE]', + description: 'legacy pen blocks - set pen color to number' + }), arguments: { HUE: { type: ArgumentType.NUMBER, @@ -366,7 +460,11 @@ class Scratch3PenBlocks { { opcode: 'changePenHueBy', blockType: BlockType.COMMAND, - text: 'change pen hue by [HUE]', + text: formatMessage({ + id: 'pen.changeHue', + default: 'change pen hue by [HUE]', + description: 'legacy pen blocks - change pen color' + }), arguments: { HUE: { type: ArgumentType.NUMBER, @@ -377,9 +475,7 @@ class Scratch3PenBlocks { } ], menus: { - colorParam: - [ColorParam.COLOR, ColorParam.SATURATION, - ColorParam.BRIGHTNESS, ColorParam.TRANSPARENCY] + colorParam: this._initColorParam() } }; } diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 994de986..7c1605d4 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -7,6 +7,7 @@ const Runtime = require('./engine/runtime'); const sb2 = require('./serialization/sb2'); const sb3 = require('./serialization/sb3'); const StringUtil = require('./util/string-util'); +const formatMessage = require('format-message'); const {loadCostume} = require('./import/load-costume.js'); const {loadSound} = require('./import/load-sound.js'); @@ -67,6 +68,9 @@ class VirtualMachine extends EventEmitter { this.runtime.on(Runtime.EXTENSION_ADDED, blocksInfo => { this.emit(Runtime.EXTENSION_ADDED, blocksInfo); }); + this.runtime.on(Runtime.BLOCKSINFO_UPDATE, blocksInfo => { + this.emit(Runtime.BLOCKSINFO_UPDATE, blocksInfo); + }); this.extensionManager = new ExtensionManager(this.runtime); @@ -552,6 +556,18 @@ class VirtualMachine extends EventEmitter { this.runtime.attachStorage(storage); } + /** + * set the current locale and builtin messages for the VM + * @param {[type]} locale current locale + * @param {[type]} messages builtin messages map for current locale + */ + setLocale (locale, messages) { + if (locale !== formatMessage.setup().locale) { + formatMessage.setup({locale: locale, translations: {[locale]: messages}}); + this.extensionManager.refreshBlocks(); + } + } + /** * Handle a Blockly event for the current editing target. * @param {!Blockly.Event} e Any Blockly event. From 25ded4cff78c9f878f5a0f7b7ad304255f271326 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 11 Dec 2017 17:01:23 -0500 Subject: [PATCH 657/721] Fix spelling of coloUrTertiary --- src/engine/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 7ab5aa2b..9c7f3e9e 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -602,7 +602,7 @@ class Runtime extends EventEmitter { category: categoryInfo.name, colour: categoryInfo.color1, colourSecondary: categoryInfo.color2, - colorTertiary: categoryInfo.color3, + colourTertiary: categoryInfo.color3, args0: [] }; From e8fd5b1f13924accce65bc5af6cfab7dfa6ba97c Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 12 Dec 2017 12:17:33 -0500 Subject: [PATCH 658/721] Replace playground with benchmark --- src/benchmark/index.html | 81 ---- src/{benchmark => playground}/benchmark.css | 0 src/{benchmark => playground}/benchmark.js | 0 src/playground/index.html | 145 ++++--- src/playground/playground.css | 73 ---- src/playground/playground.js | 402 -------------------- webpack.config.js | 73 +--- 7 files changed, 76 insertions(+), 698 deletions(-) delete mode 100644 src/benchmark/index.html rename src/{benchmark => playground}/benchmark.css (100%) rename src/{benchmark => playground}/benchmark.js (100%) delete mode 100644 src/playground/playground.css delete mode 100644 src/playground/playground.js diff --git a/src/benchmark/index.html b/src/benchmark/index.html deleted file mode 100644 index 9e6b885a..00000000 --- a/src/benchmark/index.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - Scratch VM Benchmark - - - -

Scratch VM Benchmark

-

- Welcome to the scratch-vm benchmark. This tool helps you profile a scratch - project. When you load the page, it: -

    -
  1. loads the default project -
  2. runs the project for 4 seconds to warm up -
  3. profiles for 6 seconds -
  4. stops and reports -
-

- - -

- Try a different project, like `130041250` -

- -
- -
- - 0 / 0 -
-
-
- - ... -
-
- - ... -
-
- - ... -
-
- -
- - - - - - - -
FrameSelf TimeTotal TimeExecutions
- - - - - - - - - - -
opcodeSelf TimeTotal TimeExecutions
-
- -
- - - - - - - - - - - diff --git a/src/benchmark/benchmark.css b/src/playground/benchmark.css similarity index 100% rename from src/benchmark/benchmark.css rename to src/playground/benchmark.css diff --git a/src/benchmark/benchmark.js b/src/playground/benchmark.js similarity index 100% rename from src/benchmark/benchmark.js rename to src/playground/benchmark.js diff --git a/src/playground/index.html b/src/playground/index.html index 6282014f..81ba4f57 100644 --- a/src/playground/index.html +++ b/src/playground/index.html @@ -3,88 +3,79 @@ - Scratch VM Playground - - + Scratch VM Benchmark + -
-

Scratch VM Playground

- -
- - -
-
- Turbo: -
-
- Compatibility (30 TPS): -
-
-
-
- Renderer
-
- x: - y:
- size:
- dir: - rotation style:
- visible: - -
-
- Thread explorer -

-        
-
- Block explorer -

-        
-
- Import/Export
- Project ID: - -
-

- -   - -

- -

-
+

Scratch VM Benchmark

+

+ Welcome to the scratch-vm benchmark. This tool helps you profile a scratch + project. When you load the page, it: +

    +
  1. loads the default project and enables turbo mode +
  2. runs the project for 4 seconds to warm up +
  3. profiles for 6 seconds +
  4. stops and reports +
+

+ + +

+ Try a different project, like `130041250` +

+ +
+ +
+ + 0 / 0 +
+
+
+ + ...
+
+ + ... +
+
+ + ... +
+
-
+
+ + + + + + + +
FrameSelf TimeTotal TimeExecutions
- - - - - - - - - +
+ + + + + + + + + diff --git a/src/playground/playground.css b/src/playground/playground.css deleted file mode 100644 index 3eccd262..00000000 --- a/src/playground/playground.css +++ /dev/null @@ -1,73 +0,0 @@ -body { - background: rgb(36,36,36); -} -a { - color: rgb(217,217,217); -} -h2 { - font-size: 1em; -} -#blocks { - position: absolute; - left: 40%; - right: 0; - top: 0; - bottom: 0; - font-family: "Helvetica Neue", Helvetica, sans-serif; -} -#vm-devtools { - color: rgb(217,217,217); - position: absolute; - left: 1%; - right: 60%; - top: 1%; - bottom: 0; - width: 35%; -} -#blockexplorer, #threadexplorer, #importexport { - position: absolute; - height: 75%; - overflow: scroll; - border: 1px solid #fff; - background: rgb(36,36,36); - color: rgb(217,217,217); - font-family: monospace; - font-size: 10pt; - width: 480px; - height: 360px; -} -#tab-blockexplorer, #tab-threadexplorer, #tab-importexport { - display: none; -} -#importExport { - width: 480px; - height: 360px; - background: rgb(36,36,36); - color: rgb(217,217,217); -} -#projectId { - background: rgb(36,36,36); - color: rgb(217,217,217); - font-family: monospace; - font-size: 10pt; -} -ul#playgroundLinks { - display: block; - list-style-type: none; - margin: 0; - padding: 0; - overflow: hidden; - background-color: #333; -} -#playgroundLinks li { - float: left; -} -#playgroundLinks li a { - display: block; - color: white; - text-align: center; - padding: 5px 10px; -} -#playgroundLinks li a:hover { - background-color: #111; -} diff --git a/src/playground/playground.js b/src/playground/playground.js deleted file mode 100644 index 995502d7..00000000 --- a/src/playground/playground.js +++ /dev/null @@ -1,402 +0,0 @@ -const Scratch = window.Scratch = window.Scratch || {}; - -const ASSET_SERVER = 'https://cdn.assets.scratch.mit.edu/'; -const PROJECT_SERVER = 'https://cdn.projects.scratch.mit.edu/'; - -const loadProject = function () { - let id = location.hash.substring(1); - if (id.length < 1 || !isFinite(id)) { - id = '119615668'; - } - Scratch.vm.downloadProjectId(id); -}; - -/** - * @param {Asset} asset - calculate a URL for this asset. - * @returns {string} a URL to download a project file. - */ -const getProjectUrl = function (asset) { - const assetIdParts = asset.assetId.split('.'); - const assetUrlParts = [PROJECT_SERVER, 'internalapi/project/', assetIdParts[0], '/get/']; - if (assetIdParts[1]) { - assetUrlParts.push(assetIdParts[1]); - } - return assetUrlParts.join(''); -}; - -/** - * @param {Asset} asset - calculate a URL for this asset. - * @returns {string} a URL to download a project asset (PNG, WAV, etc.) - */ -const getAssetUrl = function (asset) { - const assetUrlParts = [ - ASSET_SERVER, - 'internalapi/asset/', - asset.assetId, - '.', - asset.dataFormat, - '/get/' - ]; - return assetUrlParts.join(''); -}; - -const addProfilerPanels = function ( - stats, vm, panelName = 'BLK%', panelFg = '#fff', panelBg = '#111') { - - vm.runtime.enableProfiling(); - if (vm.runtime.profiler === null) { - // Profiler isn't available on the local system. - return; - } - - const blockPercentPanel = stats.addPanel(new window.Stats.Panel(panelName, panelFg, panelBg)); - - // Store the profiler reference for later. - const profiler = vm.runtime.profiler; - // Set profiler to null to disable profiling until later. - vm.runtime.profiler = null; - - const stepThreadsProfilerId = profiler.idByName('Sequencer.stepThreads'); - const blockFunctionProfilerId = profiler.idByName('blockFunction'); - - let blockFunctionTime = 0; - const stepValues = []; - let lastUpdate = Date.now(); - - // Collect time used by `blockFunction` calls in `execute` and add a column - // to the stats graph of the average for the last second of recordings. - profiler.onFrame = function ({id, totalTime}) { - if (id === stepThreadsProfilerId && totalTime > 0) { - // This frame wraps Sequencer.stepThreads. - - // Push the most recently summed blockFunctionTime. - stepValues.push(blockFunctionTime / totalTime * 100); - // Every second, average the pushed values and render that as a new - // column in the stats graph. - if (Date.now() - lastUpdate > 1000) { - lastUpdate = Date.now(); - const average = stepValues.reduce( - (a, b) => a + b, - 0) / stepValues.length; - blockPercentPanel.update(average, 100); - stepValues.length = 0; - } - blockFunctionTime = 0; - } else if (id === blockFunctionProfilerId) { - // This frame wraps around each blockFunction call. - blockFunctionTime += totalTime; - } - }; - - // Set the stats panel to not display by default. - blockPercentPanel.dom.style.display = 'none'; - - // When the parent of the stats graphs is clicked, check if the - // blockPercentPanel is visible. If it is visible, enable profiling by - // setting the runtime's profiler to the stored Profiler instance. If it is - // not visible, disable profiling by setting the profiler to null. - stats.dom.addEventListener('click', () => { - if (blockPercentPanel.dom.style.display === 'block') { - vm.runtime.profiler = profiler; - } else { - vm.runtime.profiler = null; - } - }); - - return blockPercentPanel; -}; - -window.onload = function () { - // Lots of global variables to make debugging easier - // Instantiate the VM. - const vm = new window.VirtualMachine(); - Scratch.vm = vm; - - const storage = new ScratchStorage(); /* global ScratchStorage */ - const AssetType = storage.AssetType; - storage.addWebSource([AssetType.Project], getProjectUrl); - storage.addWebSource([AssetType.ImageVector, AssetType.ImageBitmap, AssetType.Sound], getAssetUrl); - vm.attachStorage(storage); - - // Loading projects from the server. - document.getElementById('projectLoadButton').onclick = function () { - document.location = `#${document.getElementById('projectId').value}`; - location.reload(); - }; - loadProject(); - - // Instantiate the renderer and connect it to the VM. - const canvas = document.getElementById('scratch-stage'); - const renderer = new window.RenderWebGL(canvas); - Scratch.renderer = renderer; - vm.attachRenderer(renderer); - const audioEngine = new window.AudioEngine(); - vm.attachAudioEngine(audioEngine); - - // Instantiate scratch-blocks and attach it to the DOM. - const workspace = window.Blockly.inject('blocks', { - media: './media/', - zoom: { - controls: true, - wheel: true, - startScale: 0.75 - }, - colours: { - workspace: '#334771', - flyout: '#283856', - scrollbar: '#24324D', - scrollbarHover: '#0C111A', - insertionMarker: '#FFFFFF', - insertionMarkerOpacity: 0.3, - fieldShadow: 'rgba(255, 255, 255, 0.3)', - dragShadowOpacity: 0.6 - } - }); - Scratch.workspace = workspace; - - // Attach scratch-blocks events to VM. - workspace.addChangeListener(vm.blockListener); - workspace.addChangeListener(vm.variableListener); - const flyoutWorkspace = workspace.getFlyout().getWorkspace(); - flyoutWorkspace.addChangeListener(vm.flyoutBlockListener); - flyoutWorkspace.addChangeListener(vm.monitorBlockListener); - - // Create FPS counter. - const stats = new window.Stats(); - document.getElementById('tab-renderexplorer').appendChild(stats.dom); - stats.dom.style.position = 'relative'; - addProfilerPanels(stats, vm, 'BLK%', '#fff', '#111'); - stats.begin(); - - // Playground data tabs. - // Block representation tab. - const blockexplorer = document.getElementById('blockexplorer'); - const updateBlockExplorer = function (blocks) { - blockexplorer.innerHTML = JSON.stringify(blocks, null, 2); - window.hljs.highlightBlock(blockexplorer); - }; - - // Thread representation tab. - const threadexplorer = document.getElementById('threadexplorer'); - let cachedThreadJSON = ''; - const updateThreadExplorer = function (newJSON) { - if (newJSON !== cachedThreadJSON) { - cachedThreadJSON = newJSON; - threadexplorer.innerHTML = cachedThreadJSON; - window.hljs.highlightBlock(threadexplorer); - } - }; - - // Only request data from the VM thread if the appropriate tab is open. - Scratch.exploreTabOpen = false; - const getPlaygroundData = function () { - vm.getPlaygroundData(); - if (Scratch.exploreTabOpen) { - window.requestAnimationFrame(getPlaygroundData); - } - }; - - // VM handlers. - // Receipt of new playground data (thread, block representations). - vm.on('playgroundData', data => { - updateThreadExplorer(data.threads); - updateBlockExplorer(data.blocks); - }); - - // Receipt of new block XML for the selected target. - vm.on('workspaceUpdate', data => { - workspace.clear(); - const dom = window.Blockly.Xml.textToDom(data.xml); - window.Blockly.Xml.domToWorkspace(dom, workspace); - }); - - // Receipt of new list of targets, selected target update. - const selectedTarget = document.getElementById('selectedTarget'); - vm.on('targetsUpdate', data => { - // Clear select box. - while (selectedTarget.firstChild) { - selectedTarget.removeChild(selectedTarget.firstChild); - } - // Generate new select box. - for (let i = 0; i < data.targetList.length; i++) { - const targetOption = document.createElement('option'); - targetOption.setAttribute('value', data.targetList[i].id); - // If target id matches editingTarget id, select it. - if (data.targetList[i].id === data.editingTarget) { - targetOption.setAttribute('selected', 'selected'); - } - targetOption.appendChild( - document.createTextNode(data.targetList[i].name) - ); - selectedTarget.appendChild(targetOption); - } - }); - selectedTarget.onchange = function () { - vm.setEditingTarget(this.value); - }; - - // Feedback for stacks and blocks running. - vm.on('SCRIPT_GLOW_ON', data => { - workspace.glowStack(data.id, true); - }); - vm.on('SCRIPT_GLOW_OFF', data => { - workspace.glowStack(data.id, false); - }); - vm.on('BLOCK_GLOW_ON', data => { - workspace.glowBlock(data.id, true); - }); - vm.on('BLOCK_GLOW_OFF', data => { - workspace.glowBlock(data.id, false); - }); - vm.on('VISUAL_REPORT', data => { - workspace.reportValue(data.id, data.value); - }); - - vm.on('SPRITE_INFO_REPORT', data => { - if (data.id !== selectedTarget.value) return; // Not the editingTarget - document.getElementById('sinfo-x').value = data.x; - document.getElementById('sinfo-y').value = data.y; - document.getElementById('sinfo-size').value = data.size; - document.getElementById('sinfo-direction').value = data.direction; - document.getElementById('sinfo-rotationstyle').value = data.rotationStyle; - document.getElementById('sinfo-visible').value = data.visible; - }); - - document.getElementById('sinfo-post').addEventListener('click', () => { - const data = {}; - data.x = document.getElementById('sinfo-x').value; - data.y = document.getElementById('sinfo-y').value; - data.direction = document.getElementById('sinfo-direction').value; - data.rotationStyle = document.getElementById('sinfo-rotationstyle').value; - data.visible = document.getElementById('sinfo-visible').value === 'true'; - vm.postSpriteInfo(data); - }); - - // Feed mouse events as VM I/O events. - document.addEventListener('mousemove', e => { - const rect = canvas.getBoundingClientRect(); - const coordinates = { - x: e.clientX - rect.left, - y: e.clientY - rect.top, - canvasWidth: rect.width, - canvasHeight: rect.height - }; - Scratch.vm.postIOData('mouse', coordinates); - }); - canvas.addEventListener('mousedown', e => { - const rect = canvas.getBoundingClientRect(); - const data = { - isDown: true, - x: e.clientX - rect.left, - y: e.clientY - rect.top, - canvasWidth: rect.width, - canvasHeight: rect.height - }; - Scratch.vm.postIOData('mouse', data); - e.preventDefault(); - }); - canvas.addEventListener('mouseup', e => { - const rect = canvas.getBoundingClientRect(); - const data = { - isDown: false, - x: e.clientX - rect.left, - y: e.clientY - rect.top, - canvasWidth: rect.width, - canvasHeight: rect.height - }; - Scratch.vm.postIOData('mouse', data); - e.preventDefault(); - }); - - // Feed keyboard events as VM I/O events. - document.addEventListener('keydown', e => { - // Don't capture keys intended for Blockly inputs. - if (e.target !== document && e.target !== document.body) { - return; - } - Scratch.vm.postIOData('keyboard', { - keyCode: e.keyCode, - isDown: true - }); - e.preventDefault(); - }); - document.addEventListener('keyup', e => { - // Always capture up events, - // even those that have switched to other targets. - Scratch.vm.postIOData('keyboard', { - keyCode: e.keyCode, - isDown: false - }); - // E.g., prevent scroll. - if (e.target !== document && e.target !== document.body) { - e.preventDefault(); - } - }); - - // Run threads - vm.start(); - - // Inform VM of animation frames. - const animate = function () { - stats.update(); - requestAnimationFrame(animate); - }; - requestAnimationFrame(animate); - - // Handlers for green flag and stop all. - document.getElementById('greenflag').addEventListener('click', () => { - vm.greenFlag(); - }); - document.getElementById('stopall').addEventListener('click', () => { - vm.stopAll(); - }); - document.getElementById('turbomode').addEventListener('change', () => { - const turboOn = document.getElementById('turbomode').checked; - vm.setTurboMode(turboOn); - }); - document.getElementById('compatmode').addEventListener('change', - () => { - const compatibilityMode = document.getElementById('compatmode').checked; - vm.setCompatibilityMode(compatibilityMode); - }); - const tabBlockExplorer = document.getElementById('tab-blockexplorer'); - const tabThreadExplorer = document.getElementById('tab-threadexplorer'); - const tabRenderExplorer = document.getElementById('tab-renderexplorer'); - const tabImportExport = document.getElementById('tab-importexport'); - - // Handlers to show different explorers. - document.getElementById('threadexplorer-link').addEventListener('click', - () => { - Scratch.exploreTabOpen = true; - getPlaygroundData(); - tabBlockExplorer.style.display = 'none'; - tabRenderExplorer.style.display = 'none'; - tabThreadExplorer.style.display = 'block'; - tabImportExport.style.display = 'none'; - }); - document.getElementById('blockexplorer-link').addEventListener('click', - () => { - Scratch.exploreTabOpen = true; - getPlaygroundData(); - tabBlockExplorer.style.display = 'block'; - tabRenderExplorer.style.display = 'none'; - tabThreadExplorer.style.display = 'none'; - tabImportExport.style.display = 'none'; - }); - document.getElementById('renderexplorer-link').addEventListener('click', - () => { - Scratch.exploreTabOpen = false; - tabBlockExplorer.style.display = 'none'; - tabRenderExplorer.style.display = 'block'; - tabThreadExplorer.style.display = 'none'; - tabImportExport.style.display = 'none'; - }); - document.getElementById('importexport-link').addEventListener('click', - () => { - Scratch.exploreTabOpen = false; - tabBlockExplorer.style.display = 'none'; - tabRenderExplorer.style.display = 'none'; - tabThreadExplorer.style.display = 'none'; - tabImportExport.style.display = 'block'; - }); -}; diff --git a/webpack.config.js b/webpack.config.js index 77ae2068..0095d0dc 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -84,75 +84,14 @@ module.exports = [ 'scratch-blocks/dist/vertical.js', // Audio 'scratch-audio', + // Storage + 'scratch-storage', // Renderer 'scratch-render' ] }, output: { - libraryTarget: 'umd', - path: path.resolve('playground') - }, - module: { - rules: base.module.rules.concat([ - { - test: require.resolve('./src/index.js'), - loader: 'expose-loader?VirtualMachine' - }, - { - test: require.resolve('stats.js/build/stats.min.js'), - loader: 'script-loader' - }, - { - test: require.resolve('highlightjs/highlight.pack.min.js'), - loader: 'script-loader' - }, - { - test: require.resolve('scratch-blocks/dist/vertical.js'), - loader: 'expose-loader?Blockly' - }, - { - test: require.resolve('scratch-audio'), - loader: 'expose-loader?AudioEngine' - }, - { - test: require.resolve('scratch-render'), - loader: 'expose-loader?RenderWebGL' - } - ]) - }, - plugins: base.plugins.concat([ - new CopyWebpackPlugin([{ - from: 'node_modules/scratch-blocks/media', - to: 'media' - }, { - from: 'node_modules/highlightjs/styles/zenburn.css' - }, { - from: 'node_modules/scratch-storage/dist/web' - }, { - from: 'src/playground' - }]) - ]) - }), - // Benchmark - defaultsDeep({}, base, { - target: 'web', - entry: { - 'scratch-vm': './src/index.js', - 'vendor': [ - // FPS counter - 'stats.js/build/stats.min.js', - // Syntax highlighter - 'highlightjs/highlight.pack.min.js', - // Scratch Blocks - 'scratch-blocks/dist/vertical.js', - // Audio - 'scratch-audio', - // Renderer - 'scratch-render' - ] - }, - output: { - path: path.resolve(__dirname, 'benchmark'), + path: path.resolve(__dirname, 'playground'), filename: '[name].js' }, module: { @@ -177,6 +116,10 @@ module.exports = [ test: require.resolve('scratch-audio'), loader: 'expose-loader?AudioEngine' }, + { + test: require.resolve('scratch-storage'), + loader: 'expose-loader?ScratchStorage' + }, { test: require.resolve('scratch-render'), loader: 'expose-loader?RenderWebGL' @@ -192,7 +135,7 @@ module.exports = [ }, { from: 'node_modules/scratch-storage/dist/web' }, { - from: 'src/benchmark' + from: 'src/playground' }]) ]) }) From a586c5d9cdae593a2ed635265f4c3453d61f8aec Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Dec 2017 15:37:44 -0500 Subject: [PATCH 659/721] Revert "Fix test" This reverts commit 6b00f97804d00db2fa9b1fdb98e7577101252cf9. --- test/unit/blocks_procedures.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/blocks_procedures.js b/test/unit/blocks_procedures.js index 5dfecea7..0285b23d 100644 --- a/test/unit/blocks_procedures.js +++ b/test/unit/blocks_procedures.js @@ -16,7 +16,7 @@ test('calling a custom block with no definition does not throw', t => { } }; const util = { - getProcedureParamNamesAndIds: () => [null, null], + getProcedureParamNamesAndIds: () => null, stackFrame: { executed: false } From 0fe7eca0185dd8907c49f0657a2f0f613ede106b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Dec 2017 15:37:58 -0500 Subject: [PATCH 660/721] Fix procedure name lookup to work with undefined procedures. --- src/blocks/scratch3_procedures.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/blocks/scratch3_procedures.js b/src/blocks/scratch3_procedures.js index 3ba743d8..7fb31d28 100644 --- a/src/blocks/scratch3_procedures.js +++ b/src/blocks/scratch3_procedures.js @@ -27,15 +27,17 @@ class Scratch3ProcedureBlocks { call (args, util) { if (!util.stackFrame.executed) { const procedureCode = args.mutation.proccode; - const [paramNames, paramIds] = util.getProcedureParamNamesAndIds(procedureCode); + const paramNamesAndIds = util.getProcedureParamNamesAndIds(procedureCode); // If null, procedure could not be found, which can happen if custom // block is dragged between sprites without the definition. // Match Scratch 2.0 behavior and noop. - if (paramNames === null) { + if (paramNamesAndIds === null) { return; } + const [paramNames, paramIds] = paramNamesAndIds; + for (let i = 0; i < paramIds.length; i++) { if (args.hasOwnProperty(paramIds[i])) { util.pushParam(paramNames[i], args[paramIds[i]]); From 256d7b3d6c7d3cc044bc826d1a2a882a2e9c9f18 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 13 Dec 2017 15:53:55 -0500 Subject: [PATCH 661/721] Parse the `warp` property from the mutation instead of using directly. --- src/engine/sequencer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js index 0c796a6e..faa40b9e 100644 --- a/src/engine/sequencer.js +++ b/src/engine/sequencer.js @@ -316,7 +316,7 @@ class Sequencer { const definitionBlock = thread.target.blocks.getBlock(definition); const innerBlock = thread.target.blocks.getBlock( definitionBlock.inputs.custom_block.block); - const doWarp = innerBlock.mutation.warp; + const doWarp = JSON.parse(innerBlock.mutation.warp); if (doWarp) { thread.peekStackFrame().warpMode = true; } else if (isRecursive) { From 0f82e2a84dfef85329655e62861057795440ddca Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 14 Dec 2017 14:21:57 -0500 Subject: [PATCH 662/721] Be more cautious about parsing json. --- src/engine/sequencer.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js index faa40b9e..2db1c6ed 100644 --- a/src/engine/sequencer.js +++ b/src/engine/sequencer.js @@ -316,7 +316,15 @@ class Sequencer { const definitionBlock = thread.target.blocks.getBlock(definition); const innerBlock = thread.target.blocks.getBlock( definitionBlock.inputs.custom_block.block); - const doWarp = JSON.parse(innerBlock.mutation.warp); + let doWarp = false; + if (innerBlock && innerBlock.mutation) { + const warp = innerBlock.mutation.warp; + if (typeof warp === 'boolean') { + doWarp = warp; + } else if (typeof warp === 'string') { + doWarp = JSON.parse(warp); + } + } if (doWarp) { thread.peekStackFrame().warpMode = true; } else if (isRecursive) { From 5e6187bee79441185eb3b0595673bad058b1c983 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 14 Dec 2017 14:37:08 -0500 Subject: [PATCH 663/721] Add blockly extension for scratch extensions --- src/engine/runtime.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 9c7f3e9e..2215a02a 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -603,7 +603,8 @@ class Runtime extends EventEmitter { colour: categoryInfo.color1, colourSecondary: categoryInfo.color2, colourTertiary: categoryInfo.color3, - args0: [] + args0: [], + extensions: ['scratch_extension'] }; const inputList = []; From cbebc28307caaa9044ea460b3aa6613b9e799d90 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 14 Dec 2017 16:46:31 -0500 Subject: [PATCH 664/721] Return a promise to indicate when a costume has loaded. Parallels the way the `addSound` method works. Needed to enable selecting new assets automatically from GUI. --- src/virtual-machine.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 1c8df4df..92a8d979 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -314,9 +314,10 @@ class VirtualMachine extends EventEmitter { * @property {number} rotationCenterX - the X component of the costume's origin. * @property {number} rotationCenterY - the Y component of the costume's origin. * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume. + * @returns {?Promise} - a promise that resolves when the costume has been added */ addCostume (md5ext, costumeObject) { - loadCostume(md5ext, costumeObject, this.runtime).then(() => { + return loadCostume(md5ext, costumeObject, this.runtime).then(() => { this.editingTarget.addCostume(costumeObject); this.editingTarget.setCostume( this.editingTarget.sprite.costumes.length - 1 From 5cb0f5d76d3e2e728cfb833091b775a249e19e06 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 15 Dec 2017 14:09:29 -0500 Subject: [PATCH 665/721] Add separator to extension block definition --- src/engine/runtime.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 2215a02a..73d1e10a 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -617,16 +617,20 @@ class Runtime extends EventEmitter { blockJSON.message0 = ''; - // If an icon for the extension exists, prepend it to each block + // If an icon for the extension exists, prepend it to each block, with a vertical separator. if (categoryInfo.iconURI) { - blockJSON.message0 = '%1'; + blockJSON.message0 = '%1 %2'; const iconJSON = { type: 'field_image', src: categoryInfo.iconURI, width: 40, height: 40 }; + const separatorJSON = { + type: 'field_vertical_separator' + }; blockJSON.args0.push(iconJSON); + blockJSON.args0.push(separatorJSON); } blockJSON.message0 += blockInfo.text.replace(/\[(.+?)]/g, (match, placeholder) => { From 437cee3c14577b0f5afc22bf3b3621e283ae5dbb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 15 Dec 2017 15:52:04 -0500 Subject: [PATCH 666/721] Cast to string before asking question. Same as say blocks do. --- src/blocks/scratch3_sensing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index b23fe740..75ee571c 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -105,7 +105,7 @@ class Scratch3SensingBlocks { const _target = util.target; return new Promise(resolve => { const isQuestionAsked = this._questionList.length > 0; - this._enqueueAsk(args.QUESTION, resolve, _target, _target.visible, _target.isStage); + this._enqueueAsk(String(args.QUESTION), resolve, _target, _target.visible, _target.isStage); if (!isQuestionAsked) { this._askNextQuestion(); } From 39115169cadffcd08ee40b396af6375507901659 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 19 Dec 2017 11:11:38 -0500 Subject: [PATCH 667/721] Return promise from backdrop loading --- src/virtual-machine.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 92a8d979..0881af2c 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -444,9 +444,10 @@ class VirtualMachine extends EventEmitter { * @property {number} rotationCenterX - the X component of the backdrop's origin. * @property {number} rotationCenterY - the Y component of the backdrop's origin. * @property {number} [bitmapResolution] - the resolution scale for a bitmap backdrop. + * @returns {?Promise} - a promise that resolves when the backdrop has been added */ addBackdrop (md5ext, backdropObject) { - loadCostume(md5ext, backdropObject, this.runtime).then(() => { + return loadCostume(md5ext, backdropObject, this.runtime).then(() => { const stage = this.runtime.getTargetForStage(); stage.sprite.costumes.push(backdropObject); stage.setCostume(stage.sprite.costumes.length - 1); From 9613046b2febef2612e07d8c9e4080e2bc46895b Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 19 Dec 2017 14:47:11 -0500 Subject: [PATCH 668/721] Add optional menuIcon for extension blocks --- src/engine/runtime.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 2215a02a..c425fa33 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -475,7 +475,8 @@ class Runtime extends EventEmitter { const categoryInfo = { id: extensionInfo.id, name: extensionInfo.name, - iconURI: extensionInfo.iconURI, + blockIconURI: extensionInfo.blockIconURI, + menuIconURI: extensionInfo.menuIconURI, color1: '#FF6680', color2: '#FF4D6A', color3: '#FF3355', @@ -618,11 +619,11 @@ class Runtime extends EventEmitter { blockJSON.message0 = ''; // If an icon for the extension exists, prepend it to each block - if (categoryInfo.iconURI) { + if (categoryInfo.blockIconURI) { blockJSON.message0 = '%1'; const iconJSON = { type: 'field_image', - src: categoryInfo.iconURI, + src: categoryInfo.blockIconURI, width: 40, height: 40 }; @@ -735,7 +736,20 @@ class Runtime extends EventEmitter { for (const categoryInfo of this._blockInfo) { const {name, color1, color2} = categoryInfo; const paletteBlocks = categoryInfo.blocks.filter(block => !block.info.hideFromPalette); - xmlParts.push(``); + const colorXML = `colour="${color1}" secondaryColour="${color2}"`; + + // Use a menu icon if there is one. Otherwise, use the block icon. If there's no icon, + // the category menu will show its default colored circle. + let menuIconURI = ''; + if (categoryInfo.menuIconURI) { + menuIconURI = categoryInfo.menuIconURI; + } else if (categoryInfo.blockIconURI) { + menuIconURI = categoryInfo.blockIconURI; + } + const menuIconXML = menuIconURI ? + `iconURI="${menuIconURI}"` : ''; + + xmlParts.push(``); xmlParts.push.apply(xmlParts, paletteBlocks.map(block => block.xml)); xmlParts.push(''); } From dad620f01574b70e34c29ef1464bdba747ca0fbd Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 19 Dec 2017 14:47:37 -0500 Subject: [PATCH 669/721] Update block and menu icons for pen and music extensions --- src/extensions/scratch3_music/index.js | 12 ++++++++++-- src/extensions/scratch3_pen/index.js | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index 6d661bd2..0e14a215 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -10,7 +10,14 @@ const Timer = require('../../util/timer'); * @type {string} */ // eslint-disable-next-line max-len -const iconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PHRpdGxlPm11c2ljLWJsb2NrLWljb248L3RpdGxlPjxkZWZzPjxwYXRoIGQ9Ik0zMi4xOCAyNS44NzRDMzIuNjM2IDI4LjE1NyAzMC41MTIgMzAgMjcuNDMzIDMwYy0zLjA3IDAtNS45MjMtMS44NDMtNi4zNzItNC4xMjYtLjQ1OC0yLjI4NSAxLjY2NS00LjEzNiA0Ljc0My00LjEzNi42NDcgMCAxLjI4My4wODQgMS44OS4yMzQuMzM4LjA4Ni42MzcuMTguOTM4LjMwMi44Ny0uMDItLjEwNC0yLjI5NC0xLjgzNS0xMi4yMy0yLjEzNC0xMi4zMDIgMy4wNi0xLjg3IDguNzY4LTIuNzUyIDUuNzA4LS44ODUuMDc2IDQuODItMy42NSAzLjg0NC0zLjcyNC0uOTg3LTQuNjUtNy4xNTMuMjYzIDE0LjczOHptLTE2Ljk5OCA1Ljk5QzE1LjYzIDM0LjE0OCAxMy41MDcgMzYgMTAuNDQgMzZjLTMuMDcgMC01LjkyMi0xLjg1Mi02LjM4LTQuMTM2LS40NDgtMi4yODQgMS42NzQtNC4xMzUgNC43NS00LjEzNSAxLjAwMyAwIDEuOTc1LjE5NiAyLjg1NS41NDMuODIyLS4wNTUtLjE1LTIuMzc3LTEuODYyLTEyLjIyOC0yLjEzMy0xMi4zMDMgMy4wNi0xLjg3IDguNzY0LTIuNzUzIDUuNzA2LS44OTQuMDc2IDQuODItMy42NDggMy44MzQtMy43MjQtLjk4Ny00LjY1LTcuMTUyLjI2MiAxNC43Mzh6IiBpZD0iYSIvPjwvZGVmcz48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjx1c2UgZmlsbD0iI0ZGRiIgeGxpbms6aHJlZj0iI2EiLz48cGF0aCBzdHJva2Utb3BhY2l0eT0iLjEiIHN0cm9rZT0iIzAwMCIgZD0iTTI4LjQ1NiAyMS42NzVjLS4wMS0uMzEyLS4wODctLjgyNS0uMjU2LTEuNzAyLS4wOTYtLjQ5NS0uNjEyLTMuMDIyLS43NTMtMy43My0uMzk1LTEuOTgtLjc2LTMuOTItMS4xNDItNi4xMTMtLjczMi00LjIyMy0uNjkzLTYuMDUuMzQ0LTYuNTI3LjUtLjIzIDEuMDYtLjA4IDEuODQuMzUuNDE0LjIyNyAyLjE4MiAxLjM2NSAyLjA3IDEuMjk2IDEuOTk0IDEuMjQyIDMuNDY0IDEuNzc0IDQuOTMgMS41NDggMS41MjYtLjIzNyAyLjUwNC0uMDYgMi44NzYuNjE4LjM0OC42MzUuMDE1IDEuNDE2LS43MyAyLjE4LTEuNDcyIDEuNTE2LTMuOTc1IDIuNTE0LTUuODQ4IDIuMDIzLS44MjItLjIyLTEuMjM4LS40NjUtMi4zOC0xLjI2N2wtLjA5NS0uMDY2Yy4wNDcuNTkzLjI2NCAxLjc0LjcxNyAzLjgwMy4yOTQgMS4zMzYgMi4wOCA5LjE4NyAyLjYzNyAxMS42NzRsLjAwMi4wMTJjLjUyOCAyLjYzNy0xLjg3MyA0LjcyNC01LjIzNiA0LjcyNC0zLjI5IDAtNi4zNjMtMS45ODgtNi44NjItNC41MjgtLjUzLTIuNjQgMS44NzMtNC43MzQgNS4yMzMtNC43MzQuNjcyIDAgMS4zNDcuMDg1IDIuMDE0LjI1LjIyNy4wNTcuNDM2LjExOC42MzYuMTg3em0tMTYuOTk2IDUuOTljLS4wMS0uMzE4LS4wOS0uODM4LS4yNjYtMS43MzctLjA5LS40Ni0uNTk1LTIuOTM3LS43NTMtMy43MjctLjM5LTEuOTYtLjc1LTMuODktMS4xMy02LjA3LS43MzItNC4yMjMtLjY5Mi02LjA1LjM0NC02LjUyNi41MDItLjIzIDEuMDYtLjA4MiAxLjg0LjM1LjQxNS4yMjcgMi4xODIgMS4zNjQgMi4wNyAxLjI5NSAxLjk5MyAxLjI0MiAzLjQ2MiAxLjc3NCA0LjkyNiAxLjU0OCAxLjUyNS0uMjQgMi41MDQtLjA2NCAyLjg3Ni42MTQuMzQ4LjYzNS4wMTUgMS40MTUtLjcyOCAyLjE4LTEuNDc0IDEuNTE3LTMuOTc3IDIuNTEzLTUuODQ3IDIuMDE3LS44Mi0uMjItMS4yMzYtLjQ2NC0yLjM3OC0xLjI2N2wtLjA5NS0uMDY1Yy4wNDcuNTkzLjI2NCAxLjc0LjcxNyAzLjgwMi4yOTQgMS4zMzcgMi4wNzggOS4xOSAyLjYzNiAxMS42NzVsLjAwMy4wMTNjLjUxNyAyLjYzOC0xLjg4NCA0LjczMi01LjIzNCA0LjczMi0zLjI4NyAwLTYuMzYtMS45OTMtNi44Ny00LjU0LS41Mi0yLjY0IDEuODg0LTQuNzMgNS4yNC00LjczLjkwNSAwIDEuODAzLjE1IDIuNjUuNDM2eiIvPjwvZz48L3N2Zz4='; +const blockIconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PHRpdGxlPm11c2ljLWJsb2NrLWljb248L3RpdGxlPjxkZWZzPjxwYXRoIGQ9Ik0zMi4xOCAyNS44NzRDMzIuNjM2IDI4LjE1NyAzMC41MTIgMzAgMjcuNDMzIDMwYy0zLjA3IDAtNS45MjMtMS44NDMtNi4zNzItNC4xMjYtLjQ1OC0yLjI4NSAxLjY2NS00LjEzNiA0Ljc0My00LjEzNi42NDcgMCAxLjI4My4wODQgMS44OS4yMzQuMzM4LjA4Ni42MzcuMTguOTM4LjMwMi44Ny0uMDItLjEwNC0yLjI5NC0xLjgzNS0xMi4yMy0yLjEzNC0xMi4zMDIgMy4wNi0xLjg3IDguNzY4LTIuNzUyIDUuNzA4LS44ODUuMDc2IDQuODItMy42NSAzLjg0NC0zLjcyNC0uOTg3LTQuNjUtNy4xNTMuMjYzIDE0LjczOHptLTE2Ljk5OCA1Ljk5QzE1LjYzIDM0LjE0OCAxMy41MDcgMzYgMTAuNDQgMzZjLTMuMDcgMC01LjkyMi0xLjg1Mi02LjM4LTQuMTM2LS40NDgtMi4yODQgMS42NzQtNC4xMzUgNC43NS00LjEzNSAxLjAwMyAwIDEuOTc1LjE5NiAyLjg1NS41NDMuODIyLS4wNTUtLjE1LTIuMzc3LTEuODYyLTEyLjIyOC0yLjEzMy0xMi4zMDMgMy4wNi0xLjg3IDguNzY0LTIuNzUzIDUuNzA2LS44OTQuMDc2IDQuODItMy42NDggMy44MzQtMy43MjQtLjk4Ny00LjY1LTcuMTUyLjI2MiAxNC43Mzh6IiBpZD0iYSIvPjwvZGVmcz48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjx1c2UgZmlsbD0iI0ZGRiIgeGxpbms6aHJlZj0iI2EiLz48cGF0aCBzdHJva2Utb3BhY2l0eT0iLjEiIHN0cm9rZT0iIzAwMCIgZD0iTTI4LjQ1NiAyMS42NzVjLS4wMS0uMzEyLS4wODctLjgyNS0uMjU2LTEuNzAyLS4wOTYtLjQ5NS0uNjEyLTMuMDIyLS43NTMtMy43My0uMzk1LTEuOTgtLjc2LTMuOTItMS4xNDItNi4xMTMtLjczMi00LjIyMy0uNjkzLTYuMDUuMzQ0LTYuNTI3LjUtLjIzIDEuMDYtLjA4IDEuODQuMzUuNDE0LjIyNyAyLjE4MiAxLjM2NSAyLjA3IDEuMjk2IDEuOTk0IDEuMjQyIDMuNDY0IDEuNzc0IDQuOTMgMS41NDggMS41MjYtLjIzNyAyLjUwNC0uMDYgMi44NzYuNjE4LjM0OC42MzUuMDE1IDEuNDE2LS43MyAyLjE4LTEuNDcyIDEuNTE2LTMuOTc1IDIuNTE0LTUuODQ4IDIuMDIzLS44MjItLjIyLTEuMjM4LS40NjUtMi4zOC0xLjI2N2wtLjA5NS0uMDY2Yy4wNDcuNTkzLjI2NCAxLjc0LjcxNyAzLjgwMy4yOTQgMS4zMzYgMi4wOCA5LjE4NyAyLjYzNyAxMS42NzRsLjAwMi4wMTJjLjUyOCAyLjYzNy0xLjg3MyA0LjcyNC01LjIzNiA0LjcyNC0zLjI5IDAtNi4zNjMtMS45ODgtNi44NjItNC41MjgtLjUzLTIuNjQgMS44NzMtNC43MzQgNS4yMzMtNC43MzQuNjcyIDAgMS4zNDcuMDg1IDIuMDE0LjI1LjIyNy4wNTcuNDM2LjExOC42MzYuMTg3em0tMTYuOTk2IDUuOTljLS4wMS0uMzE4LS4wOS0uODM4LS4yNjYtMS43MzctLjA5LS40Ni0uNTk1LTIuOTM3LS43NTMtMy43MjctLjM5LTEuOTYtLjc1LTMuODktMS4xMy02LjA3LS43MzItNC4yMjMtLjY5Mi02LjA1LjM0NC02LjUyNi41MDItLjIzIDEuMDYtLjA4MiAxLjg0LjM1LjQxNS4yMjcgMi4xODIgMS4zNjQgMi4wNyAxLjI5NSAxLjk5MyAxLjI0MiAzLjQ2MiAxLjc3NCA0LjkyNiAxLjU0OCAxLjUyNS0uMjQgMi41MDQtLjA2NCAyLjg3Ni42MTQuMzQ4LjYzNS4wMTUgMS40MTUtLjcyOCAyLjE4LTEuNDc0IDEuNTE3LTMuOTc3IDIuNTEzLTUuODQ3IDIuMDE3LS44Mi0uMjItMS4yMzYtLjQ2NC0yLjM3OC0xLjI2N2wtLjA5NS0uMDY1Yy4wNDcuNTkzLjI2NCAxLjc0LjcxNyAzLjgwMi4yOTQgMS4zMzcgMi4wNzggOS4xOSAyLjYzNiAxMS42NzVsLjAwMy4wMTNjLjUxNyAyLjYzOC0xLjg4NCA0LjczMi01LjIzNCA0LjczMi0zLjI4NyAwLTYuMzYtMS45OTMtNi44Ny00LjU0LS41Mi0yLjY0IDEuODg0LTQuNzMgNS4yNC00LjczLjkwNSAwIDEuODAzLjE1IDIuNjUuNDM2eiIvPjwvZz48L3N2Zz4='; + +/** + * Icon svg to be displayed in the category menu, encoded as a data URI. + * @type {string} + */ +// eslint-disable-next-line max-len +const menuIconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE2LjA5IDEyLjkzN2MuMjI4IDEuMTQxLS44MzMgMi4wNjMtMi4zNzMgMi4wNjMtMS41MzUgMC0yLjk2Mi0uOTIyLTMuMTg2LTIuMDYzLS4yMy0xLjE0Mi44MzMtMi4wNjggMi4zNzItMi4wNjguMzIzIDAgLjY0MS4wNDIuOTQ1LjExN2EzLjUgMy41IDAgMCAxIC40NjguMTUxYy40MzUtLjAxLS4wNTItMS4xNDctLjkxNy02LjExNC0xLjA2Ny02LjE1MiAxLjUzLS45MzUgNC4zODQtMS4zNzcgMi44NTQtLjQ0Mi4wMzggMi40MS0xLjgyNSAxLjkyMi0xLjg2Mi0uNDkzLTIuMzI1LTMuNTc3LjEzMiA3LjM3ek03LjQ2IDguNTYzYy0xLjg2Mi0uNDkzLTIuMzI1LTMuNTc2LjEzIDcuMzdDNy44MTYgMTcuMDczIDYuNzU0IDE4IDUuMjIgMThjLTEuNTM1IDAtMi45NjEtLjkyNi0zLjE5LTIuMDY4LS4yMjQtMS4xNDIuODM3LTIuMDY3IDIuMzc1LTIuMDY3LjUwMSAwIC45ODcuMDk4IDEuNDI3LjI3Mi40MTItLjAyOC0uMDc0LTEuMTg5LS45My02LjExNEMzLjgzNCAxLjg3IDYuNDMgNy4wODcgOS4yODIgNi42NDZjMi44NTQtLjQ0Ny4wMzggMi40MS0xLjgyMyAxLjkxN3oiIGZpbGw9IiM1NzVFNzUiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg=='; /** * Class for the music-related blocks in Scratch 3.0 @@ -402,7 +409,8 @@ class Scratch3MusicBlocks { return { id: 'music', name: 'Music', - iconURI: iconURI, + menuIconURI: menuIconURI, + blockIconURI: blockIconURI, blocks: [ { opcode: 'playDrumForBeats', diff --git a/src/extensions/scratch3_pen/index.js b/src/extensions/scratch3_pen/index.js index e194a1d5..b3a8818d 100644 --- a/src/extensions/scratch3_pen/index.js +++ b/src/extensions/scratch3_pen/index.js @@ -13,7 +13,7 @@ const log = require('../../util/log'); * @type {string} */ // eslint-disable-next-line max-len -const iconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+cGVuLWljb248L3RpdGxlPjxnIHN0cm9rZT0iIzU3NUU3NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik04Ljc1MyAzNC42MDJsLTQuMjUgMS43OCAxLjc4My00LjIzN2MxLjIxOC0yLjg5MiAyLjkwNy01LjQyMyA1LjAzLTcuNTM4TDMxLjA2NiA0LjkzYy44NDYtLjg0MiAyLjY1LS40MSA0LjAzMi45NjcgMS4zOCAxLjM3NSAxLjgxNiAzLjE3My45NyA0LjAxNUwxNi4zMTggMjkuNTljLTIuMTIzIDIuMTE2LTQuNjY0IDMuOC03LjU2NSA1LjAxMiIgZmlsbD0iI0ZGRiIvPjxwYXRoIGQ9Ik0yOS40MSA2LjExcy00LjQ1LTIuMzc4LTguMjAyIDUuNzcyYy0xLjczNCAzLjc2Ni00LjM1IDEuNTQ2LTQuMzUgMS41NDYiLz48cGF0aCBkPSJNMzYuNDIgOC44MjVjMCAuNDYzLS4xNC44NzMtLjQzMiAxLjE2NGwtOS4zMzUgOS4zYy4yODItLjI5LjQxLS42NjguNDEtMS4xMiAwLS44NzQtLjUwNy0xLjk2My0xLjQwNi0yLjg2OC0xLjM2Mi0xLjM1OC0zLjE0Ny0xLjgtNC4wMDItLjk5TDMwLjk5IDUuMDFjLjg0NC0uODQgMi42NS0uNDEgNC4wMzUuOTYuODk4LjkwNCAxLjM5NiAxLjk4MiAxLjM5NiAyLjg1NU0xMC41MTUgMzMuNzc0Yy0uNTczLjMwMi0xLjE1Ny41Ny0xLjc2NC44M0w0LjUgMzYuMzgybDEuNzg2LTQuMjM1Yy4yNTgtLjYwNC41My0xLjE4Ni44MzMtMS43NTcuNjkuMTgzIDEuNDQ4LjYyNSAyLjEwOCAxLjI4Mi42Ni42NTggMS4xMDIgMS40MTIgMS4yODcgMi4xMDIiIGZpbGw9IiM0Qzk3RkYiLz48cGF0aCBkPSJNMzYuNDk4IDguNzQ4YzAgLjQ2NC0uMTQuODc0LS40MzMgMS4xNjVsLTE5Ljc0MiAxOS42OGMtMi4xMyAyLjExLTQuNjczIDMuNzkzLTcuNTcyIDUuMDFMNC41IDM2LjM4bC45NzQtMi4zMTYgMS45MjUtLjgwOGMyLjg5OC0xLjIxOCA1LjQ0LTIuOSA3LjU3LTUuMDFsMTkuNzQzLTE5LjY4Yy4yOTItLjI5Mi40MzItLjcwMi40MzItMS4xNjUgMC0uNjQ2LS4yNy0xLjQtLjc4LTIuMTIyLjI1LjE3Mi41LjM3Ny43MzcuNjE0Ljg5OC45MDUgMS4zOTYgMS45ODMgMS4zOTYgMi44NTYiIGZpbGw9IiM1NzVFNzUiIG9wYWNpdHk9Ii4xNSIvPjxwYXRoIGQ9Ik0xOC40NSAxMi44M2MwIC41LS40MDQuOTA1LS45MDQuOTA1cy0uOTA1LS40MDUtLjkwNS0uOTA0YzAtLjUuNDA3LS45MDMuOTA2LS45MDMuNSAwIC45MDQuNDA0LjkwNC45MDR6IiBmaWxsPSIjNTc1RTc1Ii8+PC9nPjwvc3ZnPg=='; +const blockIconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+cGVuLWljb248L3RpdGxlPjxnIHN0cm9rZT0iIzU3NUU3NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik04Ljc1MyAzNC42MDJsLTQuMjUgMS43OCAxLjc4My00LjIzN2MxLjIxOC0yLjg5MiAyLjkwNy01LjQyMyA1LjAzLTcuNTM4TDMxLjA2NiA0LjkzYy44NDYtLjg0MiAyLjY1LS40MSA0LjAzMi45NjcgMS4zOCAxLjM3NSAxLjgxNiAzLjE3My45NyA0LjAxNUwxNi4zMTggMjkuNTljLTIuMTIzIDIuMTE2LTQuNjY0IDMuOC03LjU2NSA1LjAxMiIgZmlsbD0iI0ZGRiIvPjxwYXRoIGQ9Ik0yOS40MSA2LjExcy00LjQ1LTIuMzc4LTguMjAyIDUuNzcyYy0xLjczNCAzLjc2Ni00LjM1IDEuNTQ2LTQuMzUgMS41NDYiLz48cGF0aCBkPSJNMzYuNDIgOC44MjVjMCAuNDYzLS4xNC44NzMtLjQzMiAxLjE2NGwtOS4zMzUgOS4zYy4yODItLjI5LjQxLS42NjguNDEtMS4xMiAwLS44NzQtLjUwNy0xLjk2My0xLjQwNi0yLjg2OC0xLjM2Mi0xLjM1OC0zLjE0Ny0xLjgtNC4wMDItLjk5TDMwLjk5IDUuMDFjLjg0NC0uODQgMi42NS0uNDEgNC4wMzUuOTYuODk4LjkwNCAxLjM5NiAxLjk4MiAxLjM5NiAyLjg1NU0xMC41MTUgMzMuNzc0Yy0uNTczLjMwMi0xLjE1Ny41Ny0xLjc2NC44M0w0LjUgMzYuMzgybDEuNzg2LTQuMjM1Yy4yNTgtLjYwNC41My0xLjE4Ni44MzMtMS43NTcuNjkuMTgzIDEuNDQ4LjYyNSAyLjEwOCAxLjI4Mi42Ni42NTggMS4xMDIgMS40MTIgMS4yODcgMi4xMDIiIGZpbGw9IiM0Qzk3RkYiLz48cGF0aCBkPSJNMzYuNDk4IDguNzQ4YzAgLjQ2NC0uMTQuODc0LS40MzMgMS4xNjVsLTE5Ljc0MiAxOS42OGMtMi4xMyAyLjExLTQuNjczIDMuNzkzLTcuNTcyIDUuMDFMNC41IDM2LjM4bC45NzQtMi4zMTYgMS45MjUtLjgwOGMyLjg5OC0xLjIxOCA1LjQ0LTIuOSA3LjU3LTUuMDFsMTkuNzQzLTE5LjY4Yy4yOTItLjI5Mi40MzItLjcwMi40MzItMS4xNjUgMC0uNjQ2LS4yNy0xLjQtLjc4LTIuMTIyLjI1LjE3Mi41LjM3Ny43MzcuNjE0Ljg5OC45MDUgMS4zOTYgMS45ODMgMS4zOTYgMi44NTYiIGZpbGw9IiM1NzVFNzUiIG9wYWNpdHk9Ii4xNSIvPjxwYXRoIGQ9Ik0xOC40NSAxMi44M2MwIC41LS40MDQuOTA1LS45MDQuOTA1cy0uOTA1LS40MDUtLjkwNS0uOTA0YzAtLjUuNDA3LS45MDMuOTA2LS45MDMuNSAwIC45MDQuNDA0LjkwNC45MDR6IiBmaWxsPSIjNTc1RTc1Ii8+PC9nPjwvc3ZnPg=='; /** * Enum for pen color parameter values. @@ -286,7 +286,7 @@ class Scratch3PenBlocks { return { id: 'pen', name: 'Pen', - iconURI: iconURI, + blockIconURI: blockIconURI, blocks: [ { opcode: 'clear', From a81b81251bb29eb8027abb3893e5375beedefc5d Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 19 Dec 2017 15:54:45 -0500 Subject: [PATCH 670/721] Remove current instrument from sound state --- src/blocks/scratch3_sound.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index be78ec7e..9a2eb357 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -26,7 +26,6 @@ class Scratch3SoundBlocks { static get DEFAULT_SOUND_STATE () { return { volume: 100, - currentInstrument: 0, effects: { pitch: 0, pan: 0 From 9f6cd92d9385b679bc762b927f637d87bde4a91c Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 20 Dec 2017 10:38:16 -0500 Subject: [PATCH 671/721] Clear sound effects for all targets on GF and stop --- src/blocks/scratch3_sound.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 9a2eb357..7aa2c8ad 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -9,6 +9,11 @@ class Scratch3SoundBlocks { * @type {Runtime} */ this.runtime = runtime; + + // Clear sound effects on green flag and stop button events. + this._clearEffectsForAllTargets = this._clearEffectsForAllTargets.bind(this); + this.runtime.on('PROJECT_STOP_ALL', this._clearEffectsForAllTargets); + this.runtime.on('PROJECT_START', this._clearEffectsForAllTargets); } /** @@ -194,13 +199,24 @@ class Scratch3SoundBlocks { } clearEffects (args, util) { - const soundState = this._getSoundState(util.target); + this._clearEffectsForTarget(util.target); + } + + _clearEffectsForTarget (target) { + const soundState = this._getSoundState(target); for (const effect in soundState.effects) { if (!soundState.effects.hasOwnProperty(effect)) continue; soundState.effects[effect] = 0; } - if (util.target.audioPlayer === null) return; - util.target.audioPlayer.clearEffects(); + if (target.audioPlayer === null) return; + target.audioPlayer.clearEffects(); + } + + _clearEffectsForAllTargets () { + const allTargets = this.runtime.targets; + for (let i = 0; i < allTargets.length; i++) { + this._clearEffectsForTarget(allTargets[i]); + } } setVolume (args, util) { From 8e554245c8c18e2a9115319523bdd2a45e96f11c Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 20 Dec 2017 10:56:44 -0500 Subject: [PATCH 672/721] Check for runtime --- src/blocks/scratch3_sound.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 7aa2c8ad..29b0de06 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -12,8 +12,10 @@ class Scratch3SoundBlocks { // Clear sound effects on green flag and stop button events. this._clearEffectsForAllTargets = this._clearEffectsForAllTargets.bind(this); - this.runtime.on('PROJECT_STOP_ALL', this._clearEffectsForAllTargets); - this.runtime.on('PROJECT_START', this._clearEffectsForAllTargets); + if (this.runtime) { + this.runtime.on('PROJECT_STOP_ALL', this._clearEffectsForAllTargets); + this.runtime.on('PROJECT_START', this._clearEffectsForAllTargets); + } } /** @@ -213,6 +215,7 @@ class Scratch3SoundBlocks { } _clearEffectsForAllTargets () { + if (this.runtime.targets === null) return; const allTargets = this.runtime.targets; for (let i = 0; i < allTargets.length; i++) { this._clearEffectsForTarget(allTargets[i]); From 05a1a668b3d909094a931a6feddf5c5df69dee65 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Wed, 20 Dec 2017 11:49:40 -0500 Subject: [PATCH 673/721] Accept both objects and strings for 'loadProject'. Resolves GH-733 --- src/virtual-machine.js | 12 +++---- test/integration/import-sb2-from-object.js | 38 ++++++++++++++++++++++ 2 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 test/integration/import-sb2-from-object.js diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 0881af2c..e0bb9961 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -217,16 +217,14 @@ class VirtualMachine extends EventEmitter { this.clear(); // Validate & parse - if (typeof json !== 'string') { - log.error('Failed to parse project. Non-string supplied to fromJSON.'); - return; - } - json = JSON.parse(json); - if (typeof json !== 'object') { - log.error('Failed to parse project. JSON supplied to fromJSON is not an object.'); + if (typeof json !== 'string' && typeof json !== 'object') { + log.error('Failed to parse project. Invalid type supplied to fromJSON.'); return; } + // Attempt to parse JSON if string is supplied + if (typeof json === 'string') json = JSON.parse(json); + // Establish version, deserialize, and load into runtime // @todo Support Scratch 1.4 // @todo This is an extremely naïve / dangerous way of determining version. diff --git a/test/integration/import-sb2-from-object.js b/test/integration/import-sb2-from-object.js new file mode 100644 index 00000000..c53234f1 --- /dev/null +++ b/test/integration/import-sb2-from-object.js @@ -0,0 +1,38 @@ +const path = require('path'); +const test = require('tap').test; +const makeTestStorage = require('../fixtures/make-test-storage'); +const extract = require('../fixtures/extract'); +const VirtualMachine = require('../../src/index'); + +const uri = path.resolve(__dirname, '../fixtures/default.sb2'); +const project = JSON.parse(extract(uri)); + +test('default', t => { + const vm = new VirtualMachine(); + vm.attachStorage(makeTestStorage()); + + // Evaluate playground data and exit + vm.on('playgroundData', e => { + const threads = JSON.parse(e.threads); + t.ok(threads.length === 0); + t.end(); + process.nextTick(process.exit); + }); + + // Start VM, load project, and run + t.doesNotThrow(() => { + vm.start(); + vm.clear(); + vm.setCompatibilityMode(false); + vm.setTurboMode(false); + vm.loadProject(project).then(() => { + vm.greenFlag(); + + // After two seconds, get playground data and stop + setTimeout(() => { + vm.getPlaygroundData(); + vm.stopAll(); + }, 2000); + }); + }); +}); From a6d17fcba322d9cda089087cd1dbdd35b0eea09a Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 20 Dec 2017 14:00:23 -0500 Subject: [PATCH 674/721] Stop all sounds for all targets --- src/blocks/scratch3_sound.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 29b0de06..ea8c5e86 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -167,9 +167,17 @@ class Scratch3SoundBlocks { return -1; } - stopAllSounds (args, util) { - if (util.target.audioPlayer === null) return; - util.target.audioPlayer.stopAllSounds(); + stopAllSounds () { + if (this.runtime.targets === null) return; + const allTargets = this.runtime.targets; + for (let i = 0; i < allTargets.length; i++) { + this._stopAllSoundsForTarget(allTargets[i]); + } + } + + _stopAllSoundsForTarget (target) { + if (target.audioPlayer === null) return; + target.audioPlayer.stopAllSounds(); } setEffect (args, util) { From cd66eb7f961776862efb11bf661f5063982cd77a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 20 Dec 2017 15:25:14 -0500 Subject: [PATCH 675/721] Allow size to be set through postSpriteInfo, and emit updates when it is Required to allow size to be added to sprite info panel in the GUI. --- src/sprites/rendered-target.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 9e181b95..c41b967a 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -329,6 +329,7 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } + this.runtime.requestTargetsUpdate(this); } /** @@ -862,6 +863,9 @@ class RenderedTarget extends Target { if (data.hasOwnProperty('visible')) { this.setVisible(data.visible); } + if (data.hasOwnProperty('size')) { + this.setSize(data.size); + } } /** From 2343e04ff91753491d072a9d3ff57e748c1d652f Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Thu, 21 Dec 2017 22:28:11 +0000 Subject: [PATCH 676/721] chore(package): update tap to version 11.0.1 Closes #818 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 02caa814..8e7495b8 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "script-loader": "0.7.2", "socket.io-client": "2.0.4", "stats.js": "^0.17.0", - "tap": "^10.2.0", + "tap": "^11.0.1", "tiny-worker": "^2.1.1", "webpack": "^3.10.0", "webpack-dev-server": "^2.4.1", From 413d113dda2c8ba738ab850be316f64be6123b96 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 8 Dec 2017 12:19:11 -0500 Subject: [PATCH 677/721] Adding support for blocks to be plugged into the input of broadcast blocks. SB2 Import to come. --- src/blocks/scratch3_event.js | 25 +++++++++++++++++++++---- src/engine/execute.js | 29 ++++++++++++++++++++++++++++- src/engine/target.js | 19 ++++++++++++++++++- 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/blocks/scratch3_event.js b/src/blocks/scratch3_event.js index 5ee2fab1..3b4050b6 100644 --- a/src/blocks/scratch3_event.js +++ b/src/blocks/scratch3_event.js @@ -55,9 +55,27 @@ class Scratch3EventBlocks { return false; } + /** + * Helper function to process broadcast block input (whether it's + * input from the dropdown menu or from a plugged in input block) + * @param {object} args The given arguments for the broadcast blocks + * @param {object} util The utility associated with this block. + * @return {?Variable} The broadcast message variable that matches + * the provided input. + */ + processBroadcastInput_ (args, util) { + let broadcastInput; + if (args.BROADCAST_OPTION) { + broadcastInput = util.runtime.getTargetForStage().lookupBroadcastMsg( + args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name); + } else { + broadcastInput = util.runtime.getTargetForStage().lookupBroadcastByInputValue(args.BROADCAST_INPUT.name); + } + return broadcastInput; + } + broadcast (args, util) { - const broadcastVar = util.runtime.getTargetForStage().lookupBroadcastMsg( - args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name); + const broadcastVar = this.processBroadcastInput_(args, util); if (broadcastVar) { const broadcastOption = broadcastVar.name; util.startHats('event_whenbroadcastreceived', { @@ -67,8 +85,7 @@ class Scratch3EventBlocks { } broadcastAndWait (args, util) { - const broadcastVar = util.runtime.getTargetForStage().lookupBroadcastMsg( - args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name); + const broadcastVar = this.processBroadcastInput_(args, util); if (broadcastVar) { const broadcastOption = broadcastVar.name; // Have we run before, starting threads? diff --git a/src/engine/execute.js b/src/engine/execute.js index 7dcf80d8..a4b9404d 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -2,6 +2,7 @@ const BlockUtility = require('./block-utility'); const log = require('../util/log'); const Thread = require('./thread'); const {Map} = require('immutable'); +const cast = require('../util/cast'); /** * Single BlockUtility instance reused by execute for every pritimive ran. @@ -211,7 +212,33 @@ const execute = function (sequencer, thread) { currentStackFrame.waitingReporter = null; thread.popStack(); } - argValues[inputName] = currentStackFrame.reported[inputName]; + const inputValue = currentStackFrame.reported[inputName]; + if (inputName === 'BROADCAST_INPUT') { + const broadcastInput = inputs[inputName]; + // Check if something is plugged into the broadcast block, or + // if the shadow dropdown menu is being used. + // Differentiate between these two cases by giving argValues + // a 'BROADCAST_INPUT' field or a 'BROADCAST_OPTION' field + // respectively. + if (broadcastInput.block === broadcastInput.shadow) { + // Shadow dropdown menu is being used. + // Get the appropriate information out of it. + const shadow = blockContainer.getBlock(broadcastInput.shadow); + const broadcastField = shadow.fields.BROADCAST_OPTION; + argValues.BROADCAST_OPTION = { + id: broadcastField.id, + name: broadcastField.value + }; + } else { + // Something is plugged into the broadcast input. + // Cast it to a string. We don't need an id here. + argValues.BROADCAST_INPUT = { + name: cast.toString(inputValue) + }; + } + } else { + argValues[inputName] = inputValue; + } } // Add any mutation to args (e.g., for procedures). diff --git a/src/engine/target.js b/src/engine/target.js index 4e574177..480b1e83 100644 --- a/src/engine/target.js +++ b/src/engine/target.js @@ -115,6 +115,23 @@ class Target extends EventEmitter { } } + /** + * Look up a broadcast message with the given name and return the variable + * if it exists. Does not create a new broadcast message variable if + * it doesn't exist. + * @param {string} name Name of the variable. + * @return {?Variable} Variable object. + */ + lookupBroadcastByInputValue (name) { + const vars = this.variables; + for (const propName in vars) { + if ((vars[propName].type === Variable.BROADCAST_MESSAGE_TYPE) && + (vars[propName].name.toLowerCase() === name.toLowerCase())) { + return vars[propName]; + } + } + } + /** * Look up a variable object. * Search begins for local variables; then look for globals. @@ -141,7 +158,7 @@ class Target extends EventEmitter { * Search begins for local lists; then look for globals. * @param {!string} id Id of the list. * @param {!string} name Name of the list. - * @return {!List} List object. + * @return {!Varible} Variable object representing the found/created list. */ lookupOrCreateList (id, name) { const list = this.lookupVariableById(id); From c0e4ae455c42c49927d6a66d2fcb1213cc4eefe5 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 13 Dec 2017 13:41:22 -0500 Subject: [PATCH 678/721] Streamlining broadcast block execution. When arg information is provided to execute function, corresponding broadcast message is looked up by id, if provided, or if not, then name. If neither id nor name is provided, we can't look up the broadcast message and something is wrong, so an error is logged. We no longer need to differentiate between the shadow menu vs. plugged in input cases via the argValue name (e.g. 'BROADCAST_OPTION' vs. 'BROADCAST_INPUT'). This refactor also helps with sb2 import, coming in the next commit. --- src/blocks/scratch3_event.js | 25 ++++--------------------- src/engine/execute.js | 5 +---- src/engine/target.js | 13 ++++++++++--- 3 files changed, 15 insertions(+), 28 deletions(-) diff --git a/src/blocks/scratch3_event.js b/src/blocks/scratch3_event.js index 3b4050b6..5ee2fab1 100644 --- a/src/blocks/scratch3_event.js +++ b/src/blocks/scratch3_event.js @@ -55,27 +55,9 @@ class Scratch3EventBlocks { return false; } - /** - * Helper function to process broadcast block input (whether it's - * input from the dropdown menu or from a plugged in input block) - * @param {object} args The given arguments for the broadcast blocks - * @param {object} util The utility associated with this block. - * @return {?Variable} The broadcast message variable that matches - * the provided input. - */ - processBroadcastInput_ (args, util) { - let broadcastInput; - if (args.BROADCAST_OPTION) { - broadcastInput = util.runtime.getTargetForStage().lookupBroadcastMsg( - args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name); - } else { - broadcastInput = util.runtime.getTargetForStage().lookupBroadcastByInputValue(args.BROADCAST_INPUT.name); - } - return broadcastInput; - } - broadcast (args, util) { - const broadcastVar = this.processBroadcastInput_(args, util); + const broadcastVar = util.runtime.getTargetForStage().lookupBroadcastMsg( + args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name); if (broadcastVar) { const broadcastOption = broadcastVar.name; util.startHats('event_whenbroadcastreceived', { @@ -85,7 +67,8 @@ class Scratch3EventBlocks { } broadcastAndWait (args, util) { - const broadcastVar = this.processBroadcastInput_(args, util); + const broadcastVar = util.runtime.getTargetForStage().lookupBroadcastMsg( + args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name); if (broadcastVar) { const broadcastOption = broadcastVar.name; // Have we run before, starting threads? diff --git a/src/engine/execute.js b/src/engine/execute.js index a4b9404d..13742ec7 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -217,9 +217,6 @@ const execute = function (sequencer, thread) { const broadcastInput = inputs[inputName]; // Check if something is plugged into the broadcast block, or // if the shadow dropdown menu is being used. - // Differentiate between these two cases by giving argValues - // a 'BROADCAST_INPUT' field or a 'BROADCAST_OPTION' field - // respectively. if (broadcastInput.block === broadcastInput.shadow) { // Shadow dropdown menu is being used. // Get the appropriate information out of it. @@ -232,7 +229,7 @@ const execute = function (sequencer, thread) { } else { // Something is plugged into the broadcast input. // Cast it to a string. We don't need an id here. - argValues.BROADCAST_INPUT = { + argValues.BROADCAST_OPTION = { name: cast.toString(inputValue) }; } diff --git a/src/engine/target.js b/src/engine/target.js index 480b1e83..9861c455 100644 --- a/src/engine/target.js +++ b/src/engine/target.js @@ -98,12 +98,19 @@ class Target extends EventEmitter { * if it exists. * @param {string} id Id of the variable. * @param {string} name Name of the variable. - * @return {!Variable} Variable object. + * @return {?Variable} Variable object. */ lookupBroadcastMsg (id, name) { - const broadcastMsg = this.lookupVariableById(id); + let broadcastMsg; + if (id) { + broadcastMsg = this.lookupVariableById(id); + } else if (name) { + broadcastMsg = this.lookupBroadcastByInputValue(name); + } else { + log.error('Cannot find broadcast message if neither id nor name are provided.'); + } if (broadcastMsg) { - if (broadcastMsg.name !== name) { + if (name && (broadcastMsg.name.toLowerCase() !== name.toLowerCase())) { log.error(`Found broadcast message with id: ${id}, but` + `its name, ${broadcastMsg.name} did not match expected name ${name}.`); } From 154987fbb064f64c99067d8ca2046b25c15f50ad Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 13 Dec 2017 17:15:18 -0500 Subject: [PATCH 679/721] SB2 Import functionality for broadcast blocks with pluggable inputs. --- src/serialization/sb2.js | 37 +++++++++++++++++++++++++++----- src/serialization/sb2_specmap.js | 10 +++++---- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index d19175dc..a8b750b3 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -170,12 +170,17 @@ const generateVariableIdGetter = (function () { const globalBroadcastMsgStateGenerator = (function () { let broadcastMsgNameMap = {}; + const emptyStringName = uid(); return function (topLevel) { if (topLevel) broadcastMsgNameMap = {}; return { broadcastMsgMapUpdater: function (name) { + name = name.toLowerCase(); + if (name === '') { + name = emptyStringName; + } broadcastMsgNameMap[name] = `broadcastMsgId-${name}`; - return broadcastMsgNameMap[name]; + return {name: name, id: broadcastMsgNameMap[name]}; }, globalBroadcastMsgs: broadcastMsgNameMap }; @@ -494,15 +499,33 @@ const parseBlock = function (sb2block, addBroadcastMsg, getVariableId, extension if (shadowObscured) { fieldValue = '#990000'; } + } else if (expectedArg.inputOp === 'event_broadcast_menu') { + fieldName = 'BROADCAST_OPTION'; + if (shadowObscured) { + fieldValue = ''; + } } else if (shadowObscured) { // Filled drop-down menu. fieldValue = ''; } const fields = {}; fields[fieldName] = { - name: fieldName, - value: fieldValue + name: fieldName }; + // event_broadcast_menus have some extra properties to add to the + // field and a different value than the rest + if (expectedArg.inputOp === 'event_broadcast_menu') { + if (!shadowObscured) { + const broadcastInfo = addBroadcastMsg(fieldValue); + fields[fieldName].id = broadcastInfo.id; + // Re-assign the value, because the name could have changed + // if the scratch2 message was an empty string + fields[fieldName].value = broadcastInfo.name; + } + fields[fieldName].variableType = expectedArg.variableType; + } else { + fields[fieldName].value = fieldValue; + } activeBlock.children.push({ id: inputUid, opcode: expectedArg.inputOp, @@ -530,8 +553,12 @@ const parseBlock = function (sb2block, addBroadcastMsg, getVariableId, extension activeBlock.fields[expectedArg.fieldName].id = getVariableId(providedArg); } else if (expectedArg.fieldName === 'BROADCAST_OPTION') { // add the name in this field to the broadcast msg name map - const broadcastId = addBroadcastMsg(providedArg); - activeBlock.fields[expectedArg.fieldName].id = broadcastId; + const broadcastInfo = addBroadcastMsg(providedArg); + activeBlock.fields[expectedArg.fieldName].id = broadcastInfo.id; + // Need to reassign field value using the sb3 name from broadcastInfo + // because the sb2 message name (e.g. providedArg) could have changed + // if the original (providedArg) was an empty string + activeBlock.fields[expectedArg.fieldName].value = broadcastInfo.name; } const varType = expectedArg.variableType; if (typeof varType === 'string') { diff --git a/src/serialization/sb2_specmap.js b/src/serialization/sb2_specmap.js index cc0a5696..b829fc17 100644 --- a/src/serialization/sb2_specmap.js +++ b/src/serialization/sb2_specmap.js @@ -665,8 +665,9 @@ const specMap = { opcode: 'event_broadcast', argMap: [ { - type: 'field', - fieldName: 'BROADCAST_OPTION', + type: 'input', + inputOp: 'event_broadcast_menu', + inputName: 'BROADCAST_INPUT', variableType: Variable.BROADCAST_MESSAGE_TYPE } ] @@ -675,8 +676,9 @@ const specMap = { opcode: 'event_broadcastandwait', argMap: [ { - type: 'field', - fieldName: 'BROADCAST_OPTION', + type: 'input', + inputOp: 'event_broadcast_menu', + inputName: 'BROADCAST_INPUT', variableType: Variable.BROADCAST_MESSAGE_TYPE } ] From bcbe91064370bbe52f21cc0a66b165d3cf72b7f2 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 15 Dec 2017 11:02:26 -0500 Subject: [PATCH 680/721] Translating empty string messages from sb2 into messageN, where N is an integer >= n such that messageN is a fresh name. --- src/serialization/sb2.js | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index a8b750b3..313768dc 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -170,19 +170,23 @@ const generateVariableIdGetter = (function () { const globalBroadcastMsgStateGenerator = (function () { let broadcastMsgNameMap = {}; + const allBroadcastInputsAndFields = []; const emptyStringName = uid(); return function (topLevel) { if (topLevel) broadcastMsgNameMap = {}; return { - broadcastMsgMapUpdater: function (name) { + broadcastMsgMapUpdater: function (name, block) { name = name.toLowerCase(); if (name === '') { name = emptyStringName; } broadcastMsgNameMap[name] = `broadcastMsgId-${name}`; + allBroadcastInputsAndFields.push(block); return {name: name, id: broadcastMsgNameMap[name]}; }, - globalBroadcastMsgs: broadcastMsgNameMap + globalBroadcastMsgs: broadcastMsgNameMap, + allBroadcastInputsAndFields: allBroadcastInputsAndFields, + emptyMsgName: emptyStringName }; }; }()); @@ -345,6 +349,23 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) { // all other targets have finished processing. if (target.isStage) { const allBroadcastMsgs = globalBroadcastMsgObj.globalBroadcastMsgs; + const allBroadcastMsgInputsAndFields = globalBroadcastMsgObj.allBroadcastInputsAndFields; + const emptyName = globalBroadcastMsgObj.emptyMsgName; + if (allBroadcastMsgs[emptyName]) { + // look through allBroadcastMsgs to see if 'messageN' is used + let currIndex = 1; + while (allBroadcastMsgs[`message${currIndex}`]) { + currIndex += 1; + } + const newEmptyMsgName = `message${currIndex}`; + allBroadcastMsgs[newEmptyMsgName] = allBroadcastMsgs[emptyName]; + delete allBroadcastMsgs[emptyName]; + for (let i = 0; i < allBroadcastMsgInputsAndFields.length; i++) { + if (allBroadcastMsgInputsAndFields[i].value === emptyName) { + allBroadcastMsgInputsAndFields[i].value = newEmptyMsgName; + } + } + } for (const msgName in allBroadcastMsgs) { const msgId = allBroadcastMsgs[msgName]; const newMsg = new Variable( @@ -516,7 +537,7 @@ const parseBlock = function (sb2block, addBroadcastMsg, getVariableId, extension // field and a different value than the rest if (expectedArg.inputOp === 'event_broadcast_menu') { if (!shadowObscured) { - const broadcastInfo = addBroadcastMsg(fieldValue); + const broadcastInfo = addBroadcastMsg(fieldValue, fields[fieldName]); fields[fieldName].id = broadcastInfo.id; // Re-assign the value, because the name could have changed // if the scratch2 message was an empty string @@ -553,7 +574,7 @@ const parseBlock = function (sb2block, addBroadcastMsg, getVariableId, extension activeBlock.fields[expectedArg.fieldName].id = getVariableId(providedArg); } else if (expectedArg.fieldName === 'BROADCAST_OPTION') { // add the name in this field to the broadcast msg name map - const broadcastInfo = addBroadcastMsg(providedArg); + const broadcastInfo = addBroadcastMsg(providedArg, activeBlock.fields[expectedArg.fieldName]); activeBlock.fields[expectedArg.fieldName].id = broadcastInfo.id; // Need to reassign field value using the sb3 name from broadcastInfo // because the sb2 message name (e.g. providedArg) could have changed From 720c22db0e8dacf717f7c916bc58162a13428247 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 15 Dec 2017 14:00:53 -0500 Subject: [PATCH 681/721] Cleanup/refactoring. --- src/serialization/sb2.js | 72 ++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index 313768dc..40304ce2 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -170,22 +170,22 @@ const generateVariableIdGetter = (function () { const globalBroadcastMsgStateGenerator = (function () { let broadcastMsgNameMap = {}; - const allBroadcastInputsAndFields = []; + const allBroadcastFields = []; const emptyStringName = uid(); return function (topLevel) { if (topLevel) broadcastMsgNameMap = {}; return { - broadcastMsgMapUpdater: function (name, block) { + broadcastMsgMapUpdater: function (name, field) { name = name.toLowerCase(); if (name === '') { name = emptyStringName; } broadcastMsgNameMap[name] = `broadcastMsgId-${name}`; - allBroadcastInputsAndFields.push(block); - return {name: name, id: broadcastMsgNameMap[name]}; + allBroadcastFields.push(field); + return broadcastMsgNameMap[name]; }, globalBroadcastMsgs: broadcastMsgNameMap, - allBroadcastInputsAndFields: allBroadcastInputsAndFields, + allBroadcastFields: allBroadcastFields, emptyMsgName: emptyStringName }; }; @@ -349,23 +349,31 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) { // all other targets have finished processing. if (target.isStage) { const allBroadcastMsgs = globalBroadcastMsgObj.globalBroadcastMsgs; - const allBroadcastMsgInputsAndFields = globalBroadcastMsgObj.allBroadcastInputsAndFields; - const emptyName = globalBroadcastMsgObj.emptyMsgName; - if (allBroadcastMsgs[emptyName]) { - // look through allBroadcastMsgs to see if 'messageN' is used + const allBroadcastMsgFields = globalBroadcastMsgObj.allBroadcastFields; + const oldEmptyMsgName = globalBroadcastMsgObj.emptyMsgName; + if (allBroadcastMsgs[oldEmptyMsgName]) { + // Find a fresh 'messageN' let currIndex = 1; while (allBroadcastMsgs[`message${currIndex}`]) { currIndex += 1; } const newEmptyMsgName = `message${currIndex}`; - allBroadcastMsgs[newEmptyMsgName] = allBroadcastMsgs[emptyName]; - delete allBroadcastMsgs[emptyName]; - for (let i = 0; i < allBroadcastMsgInputsAndFields.length; i++) { - if (allBroadcastMsgInputsAndFields[i].value === emptyName) { - allBroadcastMsgInputsAndFields[i].value = newEmptyMsgName; + // Add the new empty message name to the broadcast message + // name map, and assign it the old id. + // Then, delete the old entry in map. + allBroadcastMsgs[newEmptyMsgName] = allBroadcastMsgs[oldEmptyMsgName]; + delete allBroadcastMsgs[oldEmptyMsgName]; + // Now update all the broadcast message fields with + // the new empty message name. + for (let i = 0; i < allBroadcastMsgFields.length; i++) { + if (allBroadcastMsgFields[i].value === '') { + allBroadcastMsgFields[i].value = newEmptyMsgName; } } } + // Traverse the broadcast message name map and create + // broadcast messages as variables on the stage (which is this + // target). for (const msgName in allBroadcastMsgs) { const msgId = allBroadcastMsgs[msgName]; const newMsg = new Variable( @@ -531,21 +539,25 @@ const parseBlock = function (sb2block, addBroadcastMsg, getVariableId, extension } const fields = {}; fields[fieldName] = { - name: fieldName + name: fieldName, + value: fieldValue }; // event_broadcast_menus have some extra properties to add to the // field and a different value than the rest if (expectedArg.inputOp === 'event_broadcast_menu') { if (!shadowObscured) { - const broadcastInfo = addBroadcastMsg(fieldValue, fields[fieldName]); - fields[fieldName].id = broadcastInfo.id; - // Re-assign the value, because the name could have changed - // if the scratch2 message was an empty string - fields[fieldName].value = broadcastInfo.name; + // Need to update the broadcast message name map with + // the value of this field. + // Also need to provide the fields[fieldName] object, + // so that we can later update its value property, e.g. + // if sb2 message name is empty string, we will later + // replace this field's value with messageN + // once we can traverse through all the existing message names + // and come up with a fresh messageN. + const broadcastId = addBroadcastMsg(fieldValue, fields[fieldName]); + fields[fieldName].id = broadcastId; } fields[fieldName].variableType = expectedArg.variableType; - } else { - fields[fieldName].value = fieldValue; } activeBlock.children.push({ id: inputUid, @@ -573,13 +585,15 @@ const parseBlock = function (sb2block, addBroadcastMsg, getVariableId, extension // Add `id` property to variable fields activeBlock.fields[expectedArg.fieldName].id = getVariableId(providedArg); } else if (expectedArg.fieldName === 'BROADCAST_OPTION') { - // add the name in this field to the broadcast msg name map - const broadcastInfo = addBroadcastMsg(providedArg, activeBlock.fields[expectedArg.fieldName]); - activeBlock.fields[expectedArg.fieldName].id = broadcastInfo.id; - // Need to reassign field value using the sb3 name from broadcastInfo - // because the sb2 message name (e.g. providedArg) could have changed - // if the original (providedArg) was an empty string - activeBlock.fields[expectedArg.fieldName].value = broadcastInfo.name; + // Add the name in this field to the broadcast msg name map. + // Also need to provide the fields[fieldName] object, + // so that we can later update its value property, e.g. + // if sb2 message name is empty string, we will later + // replace this field's value with messageN + // once we can traverse through all the existing message names + // and come up with a fresh messageN. + const broadcastId = addBroadcastMsg(providedArg, activeBlock.fields[expectedArg.fieldName]); + activeBlock.fields[expectedArg.fieldName].id = broadcastId; } const varType = expectedArg.variableType; if (typeof varType === 'string') { From 6fc554daa5a2600f96b78f9233724d2401ac5615 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 21 Dec 2017 18:34:19 -0500 Subject: [PATCH 682/721] Implicit deletion behavior for broadcast blocks. --- src/virtual-machine.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index e0bb9961..7b997dbd 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -8,6 +8,7 @@ const sb2 = require('./serialization/sb2'); const sb3 = require('./serialization/sb3'); const StringUtil = require('./util/string-util'); const formatMessage = require('format-message'); +const Variable = require('./engine/variable'); const {loadCostume} = require('./import/load-costume.js'); const {loadSound} = require('./import/load-sound.js'); @@ -677,6 +678,35 @@ class VirtualMachine extends EventEmitter { * of the current editing target's blocks. */ emitWorkspaceUpdate () { + // Create a list of broadcast message Ids according to the stage variables + const stageVariables = this.runtime.getTargetForStage().variables; + let messageIds = []; + for (const varId in stageVariables) { + if (stageVariables[varId].type === Variable.BROADCAST_MESSAGE_TYPE) { + messageIds.push(varId); + } + } + // Go through all blocks on all targets, removing referenced + // broadcast ids from the list. + for (let i = 0; i < this.runtime.targets.length; i++) { + const currTarget = this.runtime.targets[i]; + const currBlocks = currTarget.blocks._blocks; + for (const blockId in currBlocks) { + if (currBlocks[blockId].fields.BROADCAST_OPTION) { + const id = currBlocks[blockId].fields.BROADCAST_OPTION.id; + const index = messageIds.indexOf(id); + if (index !== -1) { + messageIds = messageIds.slice(0, index) + .concat(messageIds.slice(index + 1)); + } + } + } + } + // Anything left in messageIds is not referenced by a block, so delete it. + for (let i = 0; i < messageIds.length; i++) { + const id = messageIds[i]; + delete this.runtime.getTargetForStage().variables[id]; + } const variableMap = Object.assign({}, this.runtime.getTargetForStage().variables, this.editingTarget.variables From 29c08c3583985bd1b9af6f867bdf179a17ca0ced Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 21 Dec 2017 18:44:29 -0500 Subject: [PATCH 683/721] Fixing unit test. --- test/unit/virtual-machine.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/unit/virtual-machine.js b/test/unit/virtual-machine.js index 40bd6bb5..8dd283cf 100644 --- a/test/unit/virtual-machine.js +++ b/test/unit/virtual-machine.js @@ -287,12 +287,18 @@ test('emitWorkspaceUpdate', t => { global: { toXML: () => 'global' } + }, + blocks: { + toXML: () => 'blocks' } }, { variables: { unused: { toXML: () => 'unused' } + }, + blocks: { + toXML: () => 'blocks' } }, { variables: { From 5dbffe67ac55d7cddd4fe6a0234721e3066d1eb9 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 22 Dec 2017 10:47:11 -0800 Subject: [PATCH 684/721] Fix legacy `setPenHueToNumber` and `changePenHueBy` These two blocks were not doing the same shade-oriented math as `setPenShadeToNumber`: effectively these blocks were using the new color model instead of Scratch 2.0's hue-shade model. This change pulls the hue-shade color update logic into a separate function, now called by all the legacy pen blocks. --- src/extensions/scratch3_pen/index.js | 44 ++++++++++++++++++---------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/extensions/scratch3_pen/index.js b/src/extensions/scratch3_pen/index.js index b3a8818d..e4a0ea84 100644 --- a/src/extensions/scratch3_pen/index.js +++ b/src/extensions/scratch3_pen/index.js @@ -673,6 +673,8 @@ class Scratch3PenBlocks { const hueValue = Cast.toNumber(args.HUE); const colorValue = hueValue / 2; this._setOrChangeColorParam(ColorParam.COLOR, colorValue, penState, false); + + this._legacyUpdatePenColor(penState); } /** @@ -686,6 +688,8 @@ class Scratch3PenBlocks { const hueChange = Cast.toNumber(args.HUE); const colorChange = hueChange / 2; this._setOrChangeColorParam(ColorParam.COLOR, colorChange, penState, true); + + this._legacyUpdatePenColor(penState); } /** @@ -705,25 +709,10 @@ class Scratch3PenBlocks { newShade = newShade % 200; if (newShade < 0) newShade += 200; - // Create the new color in RGB using the scratch 2 "shade" model - let rgb = Color.hsvToRgb({h: penState.color * 360 / 100, s: 1, v: 1}); - const shade = (newShade > 100) ? 200 - newShade : newShade; - if (shade < 50) { - rgb = Color.mixRgb(Color.RGB_BLACK, rgb, (10 + shade) / 60); - } else { - rgb = Color.mixRgb(rgb, Color.RGB_WHITE, (shade - 50) / 60); - } - - // Update the pen state according to new color - const hsv = Color.rgbToHsv(rgb); - penState.color = 100 * hsv.h / 360; - penState.saturation = 100 * hsv.s; - penState.brightness = 100 * hsv.v; - // And store the shade that was used to compute this new color for later use. penState._shade = newShade; - this._updatePenColor(penState); + this._legacyUpdatePenColor(penState); } /** @@ -739,6 +728,29 @@ class Scratch3PenBlocks { this.setPenShadeToNumber({SHADE: penState._shade + shadeChange}, util); } + /** + * Update the pen state's color from its hue & shade values, Scratch 2.0 style. + * @param {object} penState - update the HSV & RGB values in this pen state from its hue & shade values. + * @private + */ + _legacyUpdatePenColor (penState) { + // Create the new color in RGB using the scratch 2 "shade" model + let rgb = Color.hsvToRgb({h: penState.color * 360 / 100, s: 1, v: 1}); + const shade = (penState._shade > 100) ? 200 - penState._shade : penState._shade; + if (shade < 50) { + rgb = Color.mixRgb(Color.RGB_BLACK, rgb, (10 + shade) / 60); + } else { + rgb = Color.mixRgb(rgb, Color.RGB_WHITE, (shade - 50) / 60); + } + + // Update the pen state according to new color + const hsv = Color.rgbToHsv(rgb); + penState.color = 100 * hsv.h / 360; + penState.saturation = 100 * hsv.s; + penState.brightness = 100 * hsv.v; + + this._updatePenColor(penState); + } } module.exports = Scratch3PenBlocks; From ed1fc5f35300364e8f7902e7ef2e6d0e2dd0df0a Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 22 Dec 2017 11:08:05 -0800 Subject: [PATCH 685/721] Use Scratch 2.0 block labels for legacy blocks --- src/extensions/scratch3_pen/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/extensions/scratch3_pen/index.js b/src/extensions/scratch3_pen/index.js index e4a0ea84..abd46db1 100644 --- a/src/extensions/scratch3_pen/index.js +++ b/src/extensions/scratch3_pen/index.js @@ -446,7 +446,7 @@ class Scratch3PenBlocks { blockType: BlockType.COMMAND, text: formatMessage({ id: 'pen.setHue', - default: 'set pen hue to [HUE]', + default: 'set pen color to [HUE]', description: 'legacy pen blocks - set pen color to number' }), arguments: { @@ -462,7 +462,7 @@ class Scratch3PenBlocks { blockType: BlockType.COMMAND, text: formatMessage({ id: 'pen.changeHue', - default: 'change pen hue by [HUE]', + default: 'change pen color by [HUE]', description: 'legacy pen blocks - change pen color' }), arguments: { From d001a855719147316ec0133fd027c073de731887 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 28 Dec 2017 12:31:23 -0500 Subject: [PATCH 686/721] Add setDragMode primitive and unit test --- src/blocks/scratch3_sensing.js | 5 +++++ test/unit/blocks_sensing.js | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index 75ee571c..0ee1286e 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -40,6 +40,7 @@ class Scratch3SensingBlocks { sensing_of: this.getAttributeOf, sensing_mousex: this.getMouseX, sensing_mousey: this.getMouseY, + sensing_setdragmode: this.setDragMode, sensing_mousedown: this.getMouseDown, sensing_keypressed: this.getKeyPressed, sensing_current: this.current, @@ -162,6 +163,10 @@ class Scratch3SensingBlocks { return Math.sqrt((dx * dx) + (dy * dy)); } + setDragMode (args, util) { + util.target.draggable = args.DRAG_MODE === 'draggable'; + } + getTimer (args, util) { return util.ioQuery('clock', 'projectTimer'); } diff --git a/test/unit/blocks_sensing.js b/test/unit/blocks_sensing.js index 5c2b927a..221742b9 100644 --- a/test/unit/blocks_sensing.js +++ b/test/unit/blocks_sensing.js @@ -65,3 +65,17 @@ test('ask and answer with a visible target', t => { s.askAndWait({QUESTION: expectedQuestion}, util); }); + +test('set drag mode', t => { + const rt = new Runtime(); + const s = new Sensing(rt); + const util = {target: {draggable: true}}; + + s.setDragMode({DRAG_MODE: 'not draggable'}, util); + t.strictEqual(util.target.draggable, false); + + s.setDragMode({DRAG_MODE: 'draggable'}, util); + t.strictEqual(util.target.draggable, true); + + t.end(); +}); From fe51bd645080449310b814e328d678b8ebdda130 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 28 Dec 2017 13:14:00 -0500 Subject: [PATCH 687/721] Update layering blocks --- src/blocks/scratch3_looks.js | 22 ++++++++++++++++------ src/sprites/rendered-target.js | 25 ++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index 1429b6dd..c694fb38 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -233,8 +233,8 @@ class Scratch3LooksBlocks { looks_cleargraphiceffects: this.clearEffects, looks_changesizeby: this.changeSize, looks_setsizeto: this.setSize, - looks_gotofront: this.goToFront, - looks_gobacklayers: this.goBackLayers, + looks_gotofrontback: this.goToFrontBack, + looks_goforwardbackwardlayers: this.goForwardBackwardLayers, looks_size: this.getSize, looks_costumeorder: this.getCostumeIndex, looks_backdroporder: this.getBackdropIndex, @@ -409,14 +409,24 @@ class Scratch3LooksBlocks { util.target.setSize(size); } - goToFront (args, util) { + goToFrontBack (args, util) { if (!util.target.isStage) { - util.target.goToFront(); + if (args.FRONT_BACK === 'front') { + util.target.goToFront(); + } else { + util.target.goToBack(); + } } } - goBackLayers (args, util) { - util.target.goBackLayers(args.NUM); + goForwardBackwardLayers (args, util) { + if (!util.target.isStage) { + if (args.FORWARD_BACKWARD === 'forward') { + util.target.goForwardLayers(Cast.toNumber(args.NUM)); + } else { + util.target.goBackwardLayers(Cast.toNumber(args.NUM)); + } + } } getSize (args, util) { diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index c41b967a..85c1d536 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -699,10 +699,29 @@ class RenderedTarget extends Target { } /** - * Move back a number of layers. - * @param {number} nLayers How many layers to go back. + * Move to the back layer. */ - goBackLayers (nLayers) { + goToBack () { + if (this.renderer) { + this.renderer.setDrawableOrder(this.drawableID, -Infinity, false, 1); + } + } + + /** + * Move forward a number of layers. + * @param {number} nLayers How many layers to go forward. + */ + goForwardLayers (nLayers) { + if (this.renderer) { + this.renderer.setDrawableOrder(this.drawableID, nLayers, true, 1); + } + } + + /** + * Move backward a number of layers. + * @param {number} nLayers How many layers to go backward. + */ + goBackwardLayers (nLayers) { if (this.renderer) { this.renderer.setDrawableOrder(this.drawableID, -nLayers, true, 1); } From 477ef531ba8982c4048d0728938eefd491cf31b1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 28 Dec 2017 14:52:06 -0500 Subject: [PATCH 688/721] Update specmap and sb2 import to add new fields to layering blocks. --- src/serialization/sb2.js | 14 ++++++++++++++ src/serialization/sb2_specmap.js | 4 ++-- test/unit/sprites_rendered-target.js | 6 +++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index 40304ce2..85f55910 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -601,6 +601,20 @@ const parseBlock = function (sb2block, addBroadcastMsg, getVariableId, extension } } } + + // Updated layering blocks + if (oldOpcode === 'comeToFront') { + activeBlock.fields.FRONT_BACK = { + name: 'FRONT_BACK', + value: 'front' + }; + } else if (oldOpcode === 'goBackByLayers:') { + activeBlock.fields.FORWARD_BACKWARD = { + name: 'FORWARD_BACKWARD', + value: 'backward' + }; + } + // Special cases to generate mutations. if (oldOpcode === 'stopScripts') { // Mutation for stop block: if the argument is 'other scripts', diff --git a/src/serialization/sb2_specmap.js b/src/serialization/sb2_specmap.js index b829fc17..a8ca4354 100644 --- a/src/serialization/sb2_specmap.js +++ b/src/serialization/sb2_specmap.js @@ -345,12 +345,12 @@ const specMap = { ] }, 'comeToFront': { - opcode: 'looks_gotofront', + opcode: 'looks_gotofrontback', argMap: [ ] }, 'goBackByLayers:': { - opcode: 'looks_gobacklayers', + opcode: 'looks_goforwardbackwardlayers', argMap: [ { type: 'input', diff --git a/test/unit/sprites_rendered-target.js b/test/unit/sprites_rendered-target.js index 1592c357..332a7811 100644 --- a/test/unit/sprites_rendered-target.js +++ b/test/unit/sprites_rendered-target.js @@ -293,8 +293,12 @@ test('layers', t => { a.renderer = renderer; a.goToFront(); t.equals(a.renderer.order, 5); - a.goBackLayers(2); + a.goBackwardLayers(2); t.equals(a.renderer.order, 3); + a.goToBack(); + t.equals(a.renderer.order, 1); + a.goForwardLayers(1); + t.equals(a.renderer.order, 2); o.drawableID = 999; a.goBehindOther(o); t.equals(a.renderer.order, 1); From b726c4c18dc5032cd2827409cc21ee4f94636a01 Mon Sep 17 00:00:00 2001 From: sjhuang26 Date: Fri, 29 Dec 2017 08:31:59 -0500 Subject: [PATCH 689/721] Changed postSpriteInfo logic --- src/sprites/rendered-target.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index c41b967a..13f9bd32 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -845,11 +845,10 @@ class RenderedTarget extends Target { */ postSpriteInfo (data) { const force = data.hasOwnProperty('force') ? data.force : null; - if (data.hasOwnProperty('x')) { - this.setXY(data.x, this.y, force); - } - if (data.hasOwnProperty('y')) { - this.setXY(this.x, data.y, force); + let isXChanged = data.hasOwnProperty('x'); + let isYChanged = data.hasOwnProperty('y'); + if (isXChanged || isYChanged) { + this.setXY(isXChanged ? data.x : this.x, isYChanged ? data.y : this.y, force); } if (data.hasOwnProperty('direction')) { this.setDirection(data.direction); From f7d80459c7cb693cbc442d99cb38f0e70f6f4ffa Mon Sep 17 00:00:00 2001 From: sjhuang26 Date: Fri, 29 Dec 2017 09:01:30 -0500 Subject: [PATCH 690/721] Fixed pen behavior when dragging --- src/extensions/scratch3_pen/index.js | 16 ++++++++++------ src/sprites/rendered-target.js | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/extensions/scratch3_pen/index.js b/src/extensions/scratch3_pen/index.js index abd46db1..c6540016 100644 --- a/src/extensions/scratch3_pen/index.js +++ b/src/extensions/scratch3_pen/index.js @@ -181,14 +181,18 @@ class Scratch3PenBlocks { * @param {RenderedTarget} target - the target which has moved. * @param {number} oldX - the previous X position. * @param {number} oldY - the previous Y position. + * @param {boolean} isForce - whether the movement was forced. * @private */ - _onTargetMoved (target, oldX, oldY) { - const penSkinId = this._getPenLayerID(); - if (penSkinId >= 0) { - const penState = this._getPenState(target); - this.runtime.renderer.penLine(penSkinId, penState.penAttributes, oldX, oldY, target.x, target.y); - this.runtime.requestRedraw(); + _onTargetMoved (target, oldX, oldY, isForce) { + // Only move the pen if the movement isn't forced (ie. dragged). + if (!isForce) { + const penSkinId = this._getPenLayerID(); + if (penSkinId >= 0) { + const penState = this._getPenState(target); + this.runtime.renderer.penLine(penSkinId, penState.penAttributes, oldX, oldY, target.x, target.y); + this.runtime.requestRedraw(); + } } } diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 13f9bd32..fe9ede29 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -203,7 +203,7 @@ class RenderedTarget extends Target { this.x = x; this.y = y; } - this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY); + this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY, force); this.runtime.requestTargetsUpdate(this); } From 72dee72148d56d65d4d209b236638d8aa2f9a306 Mon Sep 17 00:00:00 2001 From: sjhuang26 Date: Fri, 29 Dec 2017 09:13:18 -0500 Subject: [PATCH 691/721] Change let to const --- src/sprites/rendered-target.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index fe9ede29..ee1109b5 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -845,8 +845,8 @@ class RenderedTarget extends Target { */ postSpriteInfo (data) { const force = data.hasOwnProperty('force') ? data.force : null; - let isXChanged = data.hasOwnProperty('x'); - let isYChanged = data.hasOwnProperty('y'); + const isXChanged = data.hasOwnProperty('x'); + const isYChanged = data.hasOwnProperty('y'); if (isXChanged || isYChanged) { this.setXY(isXChanged ? data.x : this.x, isYChanged ? data.y : this.y, force); } From 6d90a34ff14019c9997b29a40a2aed45902c3e85 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 28 Dec 2017 12:31:23 -0500 Subject: [PATCH 692/721] Add setDragMode primitive and unit test --- src/blocks/scratch3_sensing.js | 2 +- test/unit/blocks_sensing.js | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index 0ee1286e..4f977a61 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -164,7 +164,7 @@ class Scratch3SensingBlocks { } setDragMode (args, util) { - util.target.draggable = args.DRAG_MODE === 'draggable'; + util.target.setDraggable(args.DRAG_MODE === 'draggable'); } getTimer (args, util) { diff --git a/test/unit/blocks_sensing.js b/test/unit/blocks_sensing.js index 221742b9..33b48446 100644 --- a/test/unit/blocks_sensing.js +++ b/test/unit/blocks_sensing.js @@ -1,6 +1,8 @@ const test = require('tap').test; const Sensing = require('../../src/blocks/scratch3_sensing'); const Runtime = require('../../src/engine/runtime'); +const Sprite = require('../../src/sprites/sprite'); +const RenderedTarget = require('../../src/sprites/rendered-target'); test('getPrimitives', t => { const rt = new Runtime(); @@ -67,15 +69,17 @@ test('ask and answer with a visible target', t => { }); test('set drag mode', t => { - const rt = new Runtime(); - const s = new Sensing(rt); - const util = {target: {draggable: true}}; + const runtime = new Runtime(); + runtime.requestTargetsUpdate = () => {}; // noop for testing + const sensing = new Sensing(runtime); + const s = new Sprite(); + const rt = new RenderedTarget(s, runtime); - s.setDragMode({DRAG_MODE: 'not draggable'}, util); - t.strictEqual(util.target.draggable, false); + sensing.setDragMode({DRAG_MODE: 'not draggable'}, {target: rt}); + t.strictEqual(rt.draggable, false); - s.setDragMode({DRAG_MODE: 'draggable'}, util); - t.strictEqual(util.target.draggable, true); + sensing.setDragMode({DRAG_MODE: 'draggable'}, {target: rt}); + t.strictEqual(rt.draggable, true); t.end(); }); From d6f1c48c086c40518ab383a3d95d33ae5615550a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 4 Jan 2018 10:04:54 -0500 Subject: [PATCH 693/721] Add opcodes for new looks reporters with menu for costume number/name --- src/blocks/scratch3_looks.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index c694fb38..546f594d 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -236,9 +236,8 @@ class Scratch3LooksBlocks { looks_gotofrontback: this.goToFrontBack, looks_goforwardbackwardlayers: this.goForwardBackwardLayers, looks_size: this.getSize, - looks_costumeorder: this.getCostumeIndex, - looks_backdroporder: this.getBackdropIndex, - looks_backdropname: this.getBackdropName + looks_costumenumbername: this.getCostumeNumberName, + looks_backdropnumbername: this.getBackdropNumberName }; } @@ -433,18 +432,21 @@ class Scratch3LooksBlocks { return Math.round(util.target.size); } - getBackdropIndex () { - const stage = this.runtime.getTargetForStage(); - return stage.currentCostume + 1; - } - - getBackdropName () { + getBackdropNumberName (args) { const stage = this.runtime.getTargetForStage(); + if (args.NUMBER_NAME === 'number') { + return stage.currentCostume + 1; + } + // Else return name return stage.sprite.costumes[stage.currentCostume].name; } - getCostumeIndex (args, util) { - return util.target.currentCostume + 1; + getCostumeNumberName (args, util) { + if (args.NUMBER_NAME === 'number') { + return util.target.currentCostume + 1; + } + // Else return name + return util.target.sprite.costumes[util.target.currentCostume].name; } } From 63be5bc48780cc0fe94a6b7544742a446ab0c3bc Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 4 Jan 2018 10:24:13 -0500 Subject: [PATCH 694/721] Transform sb2 looks reporters to new menu blocks --- src/serialization/sb2.js | 27 ++++++++++++++++++++++++--- src/serialization/sb2_specmap.js | 6 +++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index 85f55910..1102c005 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -602,17 +602,38 @@ const parseBlock = function (sb2block, addBroadcastMsg, getVariableId, extension } } - // Updated layering blocks - if (oldOpcode === 'comeToFront') { + // Updates for blocks that have new menus (e.g. in Looks) + switch (oldOpcode) { + case 'comeToFront': activeBlock.fields.FRONT_BACK = { name: 'FRONT_BACK', value: 'front' }; - } else if (oldOpcode === 'goBackByLayers:') { + break; + case 'goBackByLayers:': activeBlock.fields.FORWARD_BACKWARD = { name: 'FORWARD_BACKWARD', value: 'backward' }; + break; + case 'backgroundIndex': + activeBlock.fields.NUMBER_NAME = { + name: 'NUMBER_NAME', + value: 'number' + }; + break; + case 'sceneName': + activeBlock.fields.NUMBER_NAME = { + name: 'NUMBER_NAME', + value: 'name' + }; + break; + case 'costumeIndex': + activeBlock.fields.NUMBER_NAME = { + name: 'NUMBER_NAME', + value: 'number' + }; + break; } // Special cases to generate mutations. diff --git a/src/serialization/sb2_specmap.js b/src/serialization/sb2_specmap.js index a8ca4354..bf386a15 100644 --- a/src/serialization/sb2_specmap.js +++ b/src/serialization/sb2_specmap.js @@ -360,12 +360,12 @@ const specMap = { ] }, 'costumeIndex': { - opcode: 'looks_costumeorder', + opcode: 'looks_costumenumbername', argMap: [ ] }, 'sceneName': { - opcode: 'looks_backdropname', + opcode: 'looks_backdropnumbername', argMap: [ ] }, @@ -390,7 +390,7 @@ const specMap = { ] }, 'backgroundIndex': { - opcode: 'looks_backdroporder', + opcode: 'looks_backdropnumbername', argMap: [ ] }, From 3afe709cb07447c4a89a26cfa06ac9314ca298dd Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 4 Jan 2018 10:42:29 -0500 Subject: [PATCH 695/721] Add unit tests for new reporter blocks in looks --- test/unit/blocks_looks.js | 52 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 test/unit/blocks_looks.js diff --git a/test/unit/blocks_looks.js b/test/unit/blocks_looks.js new file mode 100644 index 00000000..1506d7f8 --- /dev/null +++ b/test/unit/blocks_looks.js @@ -0,0 +1,52 @@ +const test = require('tap').test; +const Looks = require('../../src/blocks/scratch3_looks'); +const util = { + target: { + currentCostume: 0, // Internally, current costume is 0 indexed + sprite: { + costumes: [ + {name: 'first name'}, + {name: 'second name'}, + {name: 'third name'} + ] + } + } +}; + +const fakeRuntime = { + getTargetForStage: () => util.target, // Just return the dummy target above. + on: () => {} // Stub out listener methods used in constructor. +}; +const blocks = new Looks(fakeRuntime); + +test('getCostumeNumberName returns 1-indexed costume number', t => { + util.target.currentCostume = 0; // This is 0-indexed. + const args = {NUMBER_NAME: 'number'}; + const number = blocks.getCostumeNumberName(args, util); + t.strictEqual(number, 1); + t.end(); +}); + +test('getCostumeNumberName can return costume name', t => { + util.target.currentCostume = 0; // This is 0-indexed. + const args = {NUMBER_NAME: 'name'}; + const number = blocks.getCostumeNumberName(args, util); + t.strictEqual(number, 'first name'); + t.end(); +}); + +test('getBackdropNumberName returns 1-indexed costume number', t => { + util.target.currentCostume = 2; // This is 0-indexed. + const args = {NUMBER_NAME: 'number'}; + const number = blocks.getBackdropNumberName(args, util); + t.strictEqual(number, 3); + t.end(); +}); + +test('getBackdropNumberName can return costume name', t => { + util.target.currentCostume = 2; // This is 0-indexed. + const args = {NUMBER_NAME: 'name'}; + const number = blocks.getBackdropNumberName(args, util); + t.strictEqual(number, 'third name'); + t.end(); +}); From 0b6d7adf3e69e296662169fa3b53a9f7a95de4e5 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Mon, 8 Jan 2018 20:22:18 -0500 Subject: [PATCH 696/721] Update monitor on dropdown change --- src/engine/blocks.js | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 920128dc..487115d1 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -3,6 +3,7 @@ const mutationAdapter = require('./mutation-adapter'); const xmlEscape = require('../util/xml-escape'); const MonitorRecord = require('./monitor-record'); const Clone = require('../util/clone'); +const {Map} = require('immutable'); /** * @fileoverview @@ -382,7 +383,20 @@ class Blocks { block.fields[args.name].id = args.value; } } else { + // Changing the value in a dropdown block.fields[args.name].value = args.value; + + if (!optRuntime || !block.parent){ + break; + } + + const parent = this._blocks[block.parent]; + if (parent.isMonitored) { + optRuntime.requestUpdateMonitor(Map({ + id: parent.id, + params: this._getBlockParams(parent) + })); + } } break; case 'mutation': @@ -390,14 +404,17 @@ class Blocks { break; case 'checkbox': block.isMonitored = args.value; - if (optRuntime) { - const isSpriteSpecific = optRuntime.monitorBlockInfo.hasOwnProperty(block.opcode) && - optRuntime.monitorBlockInfo[block.opcode].isSpriteSpecific; - block.targetId = isSpriteSpecific ? optRuntime.getEditingTarget().id : null; + if (!optRuntime) { + break; } - if (optRuntime && wasMonitored && !block.isMonitored) { + + const isSpriteSpecific = optRuntime.monitorBlockInfo.hasOwnProperty(block.opcode) && + optRuntime.monitorBlockInfo[block.opcode].isSpriteSpecific; + block.targetId = isSpriteSpecific ? optRuntime.getEditingTarget().id : null; + + if (wasMonitored && !block.isMonitored) { optRuntime.requestRemoveMonitor(block.id); - } else if (optRuntime && !wasMonitored && block.isMonitored) { + } else if (!wasMonitored && block.isMonitored) { optRuntime.requestAddMonitor(MonitorRecord({ // @todo(vm#564) this will collide if multiple sprites use same block id: block.id, From bbcb6bd5b72c0cc7d9497fb6ae8929874dddf29d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 9 Jan 2018 09:39:52 -0500 Subject: [PATCH 697/721] Differentiate clamped from non clamped mouse io data (GUI#179) --- src/blocks/scratch3_motion.js | 10 +++++----- src/blocks/scratch3_sensing.js | 8 ++++---- src/io/mouse.js | 22 +++++++++++++++++++--- test/unit/io_mouse.js | 14 ++++++++++---- 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/blocks/scratch3_motion.js b/src/blocks/scratch3_motion.js index 0b206975..69919c93 100644 --- a/src/blocks/scratch3_motion.js +++ b/src/blocks/scratch3_motion.js @@ -64,8 +64,8 @@ class Scratch3MotionBlocks { let targetX = 0; let targetY = 0; if (targetName === '_mouse_') { - targetX = util.ioQuery('mouse', 'getX'); - targetY = util.ioQuery('mouse', 'getY'); + targetX = util.ioQuery('mouse', 'getClampedX'); + targetY = util.ioQuery('mouse', 'getClampedY'); } else if (targetName === '_random_') { const stageWidth = this.runtime.constructor.STAGE_WIDTH; const stageHeight = this.runtime.constructor.STAGE_HEIGHT; @@ -106,8 +106,8 @@ class Scratch3MotionBlocks { let targetX = 0; let targetY = 0; if (args.TOWARDS === '_mouse_') { - targetX = util.ioQuery('mouse', 'getX'); - targetY = util.ioQuery('mouse', 'getY'); + targetX = util.ioQuery('mouse', 'getClampedX'); + targetY = util.ioQuery('mouse', 'getClampedY'); } else { const pointTarget = this.runtime.getSpriteTargetByName(args.TOWARDS); if (!pointTarget) return; @@ -155,7 +155,7 @@ class Scratch3MotionBlocks { util.yield(); } } - + glideTo (args, util) { const targetXY = this.getTargetXY(args.TO, util); if (targetXY) { diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index 4f977a61..d8829f22 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -147,8 +147,8 @@ class Scratch3SensingBlocks { let targetX = 0; let targetY = 0; if (args.DISTANCETOMENU === '_mouse_') { - targetX = util.ioQuery('mouse', 'getX'); - targetY = util.ioQuery('mouse', 'getY'); + targetX = util.ioQuery('mouse', 'getClampedX'); + targetY = util.ioQuery('mouse', 'getClampedY'); } else { const distTarget = this.runtime.getSpriteTargetByName( args.DISTANCETOMENU @@ -176,11 +176,11 @@ class Scratch3SensingBlocks { } getMouseX (args, util) { - return util.ioQuery('mouse', 'getX'); + return util.ioQuery('mouse', 'getClampedX'); } getMouseY (args, util) { - return util.ioQuery('mouse', 'getY'); + return util.ioQuery('mouse', 'getClampedY'); } getMouseDown (args, util) { diff --git a/src/io/mouse.js b/src/io/mouse.js index fb4f299a..ca16f7cf 100644 --- a/src/io/mouse.js +++ b/src/io/mouse.js @@ -55,17 +55,33 @@ class Mouse { /** * Get the X position of the mouse. - * @return {number} Clamped X position of the mouse cursor. + * @return {number} Non-clamped X position of the mouse cursor. */ getX () { - return MathUtil.clamp(this._x, -240, 240); + return this._x; } /** * Get the Y position of the mouse. - * @return {number} Clamped Y position of the mouse cursor. + * @return {number} Non-clamped Y position of the mouse cursor. */ getY () { + return -this._y; + } + + /** + * Get the stage-clamped X position of the mouse. + * @return {number} Clamped X position of the mouse cursor. + */ + getClampedX () { + return MathUtil.clamp(this._x, -240, 240); + } + + /** + * Get the stage-clamped Y position of the mouse. + * @return {number} Clamped Y position of the mouse cursor. + */ + getClampedY () { return MathUtil.clamp(-this._y, -180, 180); } diff --git a/test/unit/io_mouse.js b/test/unit/io_mouse.js index 91978666..fc537e18 100644 --- a/test/unit/io_mouse.js +++ b/test/unit/io_mouse.js @@ -10,6 +10,8 @@ test('spec', t => { t.type(m.postData, 'function'); t.type(m.getX, 'function'); t.type(m.getY, 'function'); + t.type(m.getClampedX, 'function'); + t.type(m.getClampedY, 'function'); t.type(m.getIsDown, 'function'); t.end(); }); @@ -19,14 +21,16 @@ test('mouseUp', t => { const m = new Mouse(rt); m.postData({ - x: 1, + x: -20, y: 10, isDown: false, canvasWidth: 480, canvasHeight: 360 }); - t.strictEquals(m.getX(), -239); + t.strictEquals(m.getX(), -260); t.strictEquals(m.getY(), 170); + t.strictEquals(m.getClampedX(), -240); + t.strictEquals(m.getClampedY(), 170); t.strictEquals(m.getIsDown(), false); t.end(); }); @@ -37,13 +41,15 @@ test('mouseDown', t => { m.postData({ x: 10, - y: 100, + y: 400, isDown: true, canvasWidth: 480, canvasHeight: 360 }); t.strictEquals(m.getX(), -230); - t.strictEquals(m.getY(), 80); + t.strictEquals(m.getY(), -220); + t.strictEquals(m.getClampedX(), -230); + t.strictEquals(m.getClampedY(), -180); t.strictEquals(m.getIsDown(), true); t.end(); }); From 327d1179d9eafbecba57a7c097a9890c5f5ae1a5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 9 Jan 2018 10:36:03 -0500 Subject: [PATCH 698/721] Update the mouse io to give scratch coordinates and client coordinates Fixes GUI #642 https://github.com/LLK/scratch-gui/issues/642 Fixes GUI #179 https://github.com/LLK/scratch-gui/issues/179 --- src/blocks/scratch3_motion.js | 8 +++---- src/blocks/scratch3_sensing.js | 12 +++++----- src/io/mouse.js | 38 +++++++++++++++++++------------ src/sprites/rendered-target.js | 3 +-- test/unit/io_mouse.js | 41 ++++++++++++++++++++++++---------- 5 files changed, 64 insertions(+), 38 deletions(-) diff --git a/src/blocks/scratch3_motion.js b/src/blocks/scratch3_motion.js index 69919c93..524eab24 100644 --- a/src/blocks/scratch3_motion.js +++ b/src/blocks/scratch3_motion.js @@ -64,8 +64,8 @@ class Scratch3MotionBlocks { let targetX = 0; let targetY = 0; if (targetName === '_mouse_') { - targetX = util.ioQuery('mouse', 'getClampedX'); - targetY = util.ioQuery('mouse', 'getClampedY'); + targetX = util.ioQuery('mouse', 'getScratchX'); + targetY = util.ioQuery('mouse', 'getScratchY'); } else if (targetName === '_random_') { const stageWidth = this.runtime.constructor.STAGE_WIDTH; const stageHeight = this.runtime.constructor.STAGE_HEIGHT; @@ -106,8 +106,8 @@ class Scratch3MotionBlocks { let targetX = 0; let targetY = 0; if (args.TOWARDS === '_mouse_') { - targetX = util.ioQuery('mouse', 'getClampedX'); - targetY = util.ioQuery('mouse', 'getClampedY'); + targetX = util.ioQuery('mouse', 'getScratchX'); + targetY = util.ioQuery('mouse', 'getScratchY'); } else { const pointTarget = this.runtime.getSpriteTargetByName(args.TOWARDS); if (!pointTarget) return; diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index d8829f22..d1f7ae51 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -120,8 +120,8 @@ class Scratch3SensingBlocks { touchingObject (args, util) { const requestedObject = args.TOUCHINGOBJECTMENU; if (requestedObject === '_mouse_') { - const mouseX = util.ioQuery('mouse', 'getX'); - const mouseY = util.ioQuery('mouse', 'getY'); + const mouseX = util.ioQuery('mouse', 'getClientX'); + const mouseY = util.ioQuery('mouse', 'getClientY'); return util.target.isTouchingPoint(mouseX, mouseY); } else if (requestedObject === '_edge_') { return util.target.isTouchingEdge(); @@ -147,8 +147,8 @@ class Scratch3SensingBlocks { let targetX = 0; let targetY = 0; if (args.DISTANCETOMENU === '_mouse_') { - targetX = util.ioQuery('mouse', 'getClampedX'); - targetY = util.ioQuery('mouse', 'getClampedY'); + targetX = util.ioQuery('mouse', 'getScratchX'); + targetY = util.ioQuery('mouse', 'getScratchY'); } else { const distTarget = this.runtime.getSpriteTargetByName( args.DISTANCETOMENU @@ -176,11 +176,11 @@ class Scratch3SensingBlocks { } getMouseX (args, util) { - return util.ioQuery('mouse', 'getClampedX'); + return util.ioQuery('mouse', 'getScratchX'); } getMouseY (args, util) { - return util.ioQuery('mouse', 'getClampedY'); + return util.ioQuery('mouse', 'getScratchY'); } getMouseDown (args, util) { diff --git a/src/io/mouse.js b/src/io/mouse.js index ca16f7cf..ecff2db1 100644 --- a/src/io/mouse.js +++ b/src/io/mouse.js @@ -40,10 +40,20 @@ class Mouse { */ postData (data) { if (data.x) { - this._x = data.x - (data.canvasWidth / 2); + this._clientX = data.x; + this._scratchX = MathUtil.clamp( + 480 * ((data.x / data.canvasWidth) - 0.5), + -240, + 240 + ); } if (data.y) { - this._y = data.y - (data.canvasHeight / 2); + this._clientY = data.y; + this._scratchY = MathUtil.clamp( + -360 * ((data.y / data.canvasHeight) - 0.5), + -180, + 180 + ); } if (typeof data.isDown !== 'undefined') { this._isDown = data.isDown; @@ -54,35 +64,35 @@ class Mouse { } /** - * Get the X position of the mouse. + * Get the X position of the mouse in client coordinates. * @return {number} Non-clamped X position of the mouse cursor. */ - getX () { - return this._x; + getClientX () { + return this._clientX; } /** - * Get the Y position of the mouse. + * Get the Y position of the mouse in client coordinates. * @return {number} Non-clamped Y position of the mouse cursor. */ - getY () { - return -this._y; + getClientY () { + return this._clientY; } /** - * Get the stage-clamped X position of the mouse. + * Get the X position of the mouse in scratch coordinates. * @return {number} Clamped X position of the mouse cursor. */ - getClampedX () { - return MathUtil.clamp(this._x, -240, 240); + getScratchX () { + return this._scratchX; } /** - * Get the stage-clamped Y position of the mouse. + * Get the Y position of the mouse in scratch coordinates. * @return {number} Clamped Y position of the mouse cursor. */ - getClampedY () { - return MathUtil.clamp(-this._y, -180, 180); + getScratchY () { + return this._scratchY; } /** diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 1288240d..b6edd06c 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -616,8 +616,7 @@ class RenderedTarget extends Target { // Limits test to this Drawable, so this will return true // even if the clone is obscured by another Drawable. const pickResult = this.runtime.renderer.pick( - x + (this.runtime.constructor.STAGE_WIDTH / 2), - -y + (this.runtime.constructor.STAGE_HEIGHT / 2), + x, y, null, null, [this.drawableID] ); diff --git a/test/unit/io_mouse.js b/test/unit/io_mouse.js index fc537e18..48613fdb 100644 --- a/test/unit/io_mouse.js +++ b/test/unit/io_mouse.js @@ -8,10 +8,10 @@ test('spec', t => { t.type(m, 'object'); t.type(m.postData, 'function'); - t.type(m.getX, 'function'); - t.type(m.getY, 'function'); - t.type(m.getClampedX, 'function'); - t.type(m.getClampedY, 'function'); + t.type(m.getClientX, 'function'); + t.type(m.getClientY, 'function'); + t.type(m.getScratchX, 'function'); + t.type(m.getScratchY, 'function'); t.type(m.getIsDown, 'function'); t.end(); }); @@ -27,10 +27,10 @@ test('mouseUp', t => { canvasWidth: 480, canvasHeight: 360 }); - t.strictEquals(m.getX(), -260); - t.strictEquals(m.getY(), 170); - t.strictEquals(m.getClampedX(), -240); - t.strictEquals(m.getClampedY(), 170); + t.strictEquals(m.getClientX(), -20); + t.strictEquals(m.getClientY(), 10); + t.strictEquals(m.getScratchX(), -240); + t.strictEquals(m.getScratchY(), 170); t.strictEquals(m.getIsDown(), false); t.end(); }); @@ -46,10 +46,27 @@ test('mouseDown', t => { canvasWidth: 480, canvasHeight: 360 }); - t.strictEquals(m.getX(), -230); - t.strictEquals(m.getY(), -220); - t.strictEquals(m.getClampedX(), -230); - t.strictEquals(m.getClampedY(), -180); + t.strictEquals(m.getClientX(), 10); + t.strictEquals(m.getClientY(), 400); + t.strictEquals(m.getScratchX(), -230); + t.strictEquals(m.getScratchY(), -180); t.strictEquals(m.getIsDown(), true); t.end(); }); + +test('at zoomed scale', t => { + const rt = new Runtime(); + const m = new Mouse(rt); + + m.postData({ + x: 240, + y: 540, + canvasWidth: 960, + canvasHeight: 720 + }); + t.strictEquals(m.getClientX(), 240); + t.strictEquals(m.getClientY(), 540); + t.strictEquals(m.getScratchX(), -120); + t.strictEquals(m.getScratchY(), -90); + t.end(); +}); From db662b8584e2eb2de1ac3793f2f54ffba074f840 Mon Sep 17 00:00:00 2001 From: DD Date: Tue, 9 Jan 2018 14:55:46 -0500 Subject: [PATCH 699/721] fix lint --- src/engine/blocks.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 487115d1..d5e734ce 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -402,7 +402,7 @@ class Blocks { case 'mutation': block.mutation = mutationAdapter(args.value); break; - case 'checkbox': + case 'checkbox': { block.isMonitored = args.value; if (!optRuntime) { break; @@ -428,6 +428,7 @@ class Blocks { } break; } + } this.resetCache(); } From 7c0117e9edb286bd8816de750cb4377b96c8d833 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 9 Jan 2018 15:56:58 -0500 Subject: [PATCH 700/721] =?UTF-8?q?Clones=20get=20synced=20to=20parent?= =?UTF-8?q?=E2=80=99s=20sound=20state?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/blocks/scratch3_sound.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index ea8c5e86..48d25dde 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -16,6 +16,9 @@ class Scratch3SoundBlocks { this.runtime.on('PROJECT_STOP_ALL', this._clearEffectsForAllTargets); this.runtime.on('PROJECT_START', this._clearEffectsForAllTargets); } + + this._onTargetCreated = this._onTargetCreated.bind(this); + runtime.on('targetWasCreated', this._onTargetCreated); } /** @@ -88,6 +91,23 @@ class Scratch3SoundBlocks { return soundState; } + /** + * When a Target is cloned, clone the sound state. + * @param {Target} newTarget - the newly created target. + * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any. + * @listens Runtime#event:targetWasCreated + * @private + */ + _onTargetCreated (newTarget, sourceTarget) { + if (sourceTarget) { + const soundState = this._getSoundState(sourceTarget); + if (soundState && newTarget) { + newTarget.setCustomState(Scratch3SoundBlocks.STATE_KEY, Clone.simple(soundState)); + this._syncEffectsForTarget(newTarget); + } + } + } + /** * Retrieve the block primitives implemented by this package. * @return {object.} Mapping of opcode to Function. @@ -208,6 +228,15 @@ class Scratch3SoundBlocks { util.target.audioPlayer.setEffect(effect, soundState.effects[effect]); } + _syncEffectsForTarget (target) { + if (!target || !target.audioPlayer) return; + const soundState = this._getSoundState(target); + for (const effect in soundState.effects) { + if (!soundState.effects.hasOwnProperty(effect)) continue; + target.audioPlayer.setEffect(effect, soundState.effects[effect]); + } + } + clearEffects (args, util) { this._clearEffectsForTarget(util.target); } From 283804c21c859564da1ddc3870746dc65d8e1522 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 9 Jan 2018 15:57:33 -0500 Subject: [PATCH 701/721] Init and dispose audio player --- src/sprites/rendered-target.js | 18 ++++++++++-------- src/sprites/sprite.js | 1 + 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 1288240d..4a1e7cdd 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -131,17 +131,15 @@ class RenderedTarget extends Target { 'control_start_as_clone', null, this ); } + } - /** - * Audio player - */ + /** + * Initialize the audio player for this sprite or clone. + */ + initAudio () { this.audioPlayer = null; if (this.runtime && this.runtime.audioEngine) { - if (this.isOriginal) { - this.audioPlayer = this.runtime.audioEngine.createPlayer(); - } else { - this.audioPlayer = this.sprite.clones[0].audioPlayer; - } + this.audioPlayer = this.runtime.audioEngine.createPlayer(); } } @@ -942,6 +940,10 @@ class RenderedTarget extends Target { this.runtime.requestRedraw(); } } + if (this.audioPlayer) { + this.audioPlayer.stopAllSounds(); + this.audioPlayer.dispose(); + } } } diff --git a/src/sprites/sprite.js b/src/sprites/sprite.js index 26818434..bf68c98d 100644 --- a/src/sprites/sprite.js +++ b/src/sprites/sprite.js @@ -56,6 +56,7 @@ class Sprite { const newClone = new RenderedTarget(this, this.runtime); newClone.isOriginal = this.clones.length === 0; this.clones.push(newClone); + newClone.initAudio(); if (newClone.isOriginal) { newClone.initDrawable(); this.runtime.fireTargetWasCreated(newClone); From eca66e9ddebebd5398e459a8bc40a5d0b5ae33b1 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 9 Jan 2018 19:22:33 -0500 Subject: [PATCH 702/721] Check for runtime --- src/blocks/scratch3_sound.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 48d25dde..1d7cfa23 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -18,7 +18,9 @@ class Scratch3SoundBlocks { } this._onTargetCreated = this._onTargetCreated.bind(this); - runtime.on('targetWasCreated', this._onTargetCreated); + if (this.runtime) { + runtime.on('targetWasCreated', this._onTargetCreated); + } } /** From fce83dc2c3ebb633568c7b9183e16ed87209dc1e Mon Sep 17 00:00:00 2001 From: DD Date: Wed, 10 Jan 2018 17:50:48 -0500 Subject: [PATCH 703/721] make it work for non-droppable inputs --- src/engine/blocks.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/engine/blocks.js b/src/engine/blocks.js index d5e734ce..b889f201 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -386,15 +386,15 @@ class Blocks { // Changing the value in a dropdown block.fields[args.name].value = args.value; - if (!optRuntime || !block.parent){ + if (!optRuntime){ break; } - const parent = this._blocks[block.parent]; - if (parent.isMonitored) { + const flyoutBlock = block.shadow && block.parent ? this._blocks[block.parent] : block; + if (flyoutBlock.isMonitored) { optRuntime.requestUpdateMonitor(Map({ - id: parent.id, - params: this._getBlockParams(parent) + id: flyoutBlock.id, + params: this._getBlockParams(flyoutBlock) })); } } From 323aafd165cd870d6ec4d3ca0453c389f55a284c Mon Sep 17 00:00:00 2001 From: DD Date: Wed, 10 Jan 2018 17:57:04 -0500 Subject: [PATCH 704/721] Update VM's model of monitored blocks --- src/blocks/scratch3_looks.js | 5 ++--- src/blocks/scratch3_sensing.js | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index 546f594d..e173ca04 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -244,9 +244,8 @@ class Scratch3LooksBlocks { getMonitored () { return { looks_size: {isSpriteSpecific: true}, - looks_costumeorder: {isSpriteSpecific: true}, - looks_backdroporder: {}, - looks_backdropname: {} + looks_costumenumbername: {isSpriteSpecific: true}, + looks_backdropnumbername: {} }; } diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index 4f977a61..0cd4dde6 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -56,7 +56,6 @@ class Scratch3SensingBlocks { sensing_answer: {}, sensing_loudness: {}, sensing_timer: {}, - sensing_of: {}, sensing_current: {} }; } From bc64e6ed3e7f950c8b4825bfa7063936252812a7 Mon Sep 17 00:00:00 2001 From: DD Date: Thu, 11 Jan 2018 11:28:21 -0500 Subject: [PATCH 705/721] Unique sprite and backdrop names --- src/virtual-machine.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 7b997dbd..d1d8c2a1 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -268,6 +268,8 @@ class VirtualMachine extends EventEmitter { targets.forEach(target => { this.runtime.targets.push(target); (/** @type RenderedTarget */ target).updateAllDrawableProperties(); + // Ensure unique sprite name + if (target.isSprite()) this.renameSprite(target.id, target.getName()); }); // Select the first target for editing, e.g., the first sprite. if (wholeProject && (targets.length > 1)) { @@ -448,7 +450,7 @@ class VirtualMachine extends EventEmitter { addBackdrop (md5ext, backdropObject) { return loadCostume(md5ext, backdropObject, this.runtime).then(() => { const stage = this.runtime.getTargetForStage(); - stage.sprite.costumes.push(backdropObject); + stage.addCostume(backdropObject); stage.setCostume(stage.sprite.costumes.length - 1); }); } From 7ed58da1cff4517f8917ee5f3dd1817c59265236 Mon Sep 17 00:00:00 2001 From: DD Date: Thu, 11 Jan 2018 16:43:18 -0500 Subject: [PATCH 706/721] fix set editing target function --- src/virtual-machine.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index d1d8c2a1..61b61e51 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -631,7 +631,7 @@ class VirtualMachine extends EventEmitter { */ setEditingTarget (targetId) { // Has the target id changed? If not, exit. - if (targetId === this.editingTarget.id) { + if (this.editingTarget && targetId === this.editingTarget.id) { return; } const target = this.runtime.getTargetById(targetId); From 223575f8505a10050bb5b427ab66953171c4e89c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 12 Jan 2018 11:20:17 -0500 Subject: [PATCH 707/721] Remove extra call to initDrawable that was erasing existing drawable. initDrawable is called in createClone previously, where the new costumes are installed. Calling this again here erases those loaded skins and causes an error when trying to calculate the uniforms when a drag starts. --- src/sprites/rendered-target.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 1288240d..fbd6b4bc 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -831,7 +831,6 @@ class RenderedTarget extends Target { newTarget.effects = JSON.parse(JSON.stringify(this.effects)); newTarget.variables = JSON.parse(JSON.stringify(this.variables)); newTarget.lists = JSON.parse(JSON.stringify(this.lists)); - newTarget.initDrawable(); newTarget.updateAllDrawableProperties(); newTarget.goBehindOther(this); return newTarget; From 57f3d43f4f2e4bcf15e0daae5b91ddbcedc59a9d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Sat, 13 Jan 2018 13:23:33 -0500 Subject: [PATCH 708/721] Update specmap for sensing blocks that are now fixed menus. --- src/serialization/sb2_specmap.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/serialization/sb2_specmap.js b/src/serialization/sb2_specmap.js index bf386a15..e9b56b9d 100644 --- a/src/serialization/sb2_specmap.js +++ b/src/serialization/sb2_specmap.js @@ -861,9 +861,8 @@ const specMap = { opcode: 'sensing_keypressed', argMap: [ { - type: 'input', - inputOp: 'sensing_keyoptions', - inputName: 'KEY_OPTION' + type: 'field', + fieldName: 'KEY_OPTION' } ] }, @@ -936,9 +935,8 @@ const specMap = { opcode: 'sensing_of', argMap: [ { - type: 'input', - inputOp: 'sensing_of_property_menu', - inputName: 'PROPERTY' + type: 'field', + fieldName: 'PROPERTY' }, { type: 'input', From 2c1412dda42f32c2cd67c39aa91c09d20e09f5bd Mon Sep 17 00:00:00 2001 From: DD Date: Tue, 16 Jan 2018 17:41:18 -0500 Subject: [PATCH 709/721] Use text-encoding library for text encoder --- package.json | 1 + src/virtual-machine.js | 1 + 2 files changed, 2 insertions(+) diff --git a/package.json b/package.json index 02caa814..e1a5af55 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "socket.io-client": "2.0.4", "stats.js": "^0.17.0", "tap": "^10.2.0", + "text-encoding": "0.6.4", "tiny-worker": "^2.1.1", "webpack": "^3.10.0", "webpack-dev-server": "^2.4.1", diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 61b61e51..5cacafee 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -1,3 +1,4 @@ +import {TextEncoder} from 'text-encoding'; const EventEmitter = require('events'); const centralDispatch = require('./dispatch/central-dispatch'); From c0db708986f7f3d511122e8459c531370e75fb33 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 17 Jan 2018 09:47:01 -0500 Subject: [PATCH 710/721] Fix import syntax --- src/virtual-machine.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 5cacafee..0612261a 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -1,4 +1,4 @@ -import {TextEncoder} from 'text-encoding'; +const TextEncoder = require('text-encoding'); const EventEmitter = require('events'); const centralDispatch = require('./dispatch/central-dispatch'); From e83141e81068f68b6cc8b3960ffc8ae836c7813e Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 17 Jan 2018 10:59:28 -0500 Subject: [PATCH 711/721] Revert "Use text-encoding library for text encoder" --- package.json | 1 - src/virtual-machine.js | 1 - 2 files changed, 2 deletions(-) diff --git a/package.json b/package.json index e1a5af55..02caa814 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,6 @@ "socket.io-client": "2.0.4", "stats.js": "^0.17.0", "tap": "^10.2.0", - "text-encoding": "0.6.4", "tiny-worker": "^2.1.1", "webpack": "^3.10.0", "webpack-dev-server": "^2.4.1", diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 0612261a..61b61e51 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -1,4 +1,3 @@ -const TextEncoder = require('text-encoding'); const EventEmitter = require('events'); const centralDispatch = require('./dispatch/central-dispatch'); From d649b2555b09c4fae3f1fa2d469b67923848ae0d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 17 Jan 2018 11:31:44 -0500 Subject: [PATCH 712/721] Require TextEncoder correctly --- package.json | 1 + src/virtual-machine.js | 1 + 2 files changed, 2 insertions(+) diff --git a/package.json b/package.json index 02caa814..e1a5af55 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "socket.io-client": "2.0.4", "stats.js": "^0.17.0", "tap": "^10.2.0", + "text-encoding": "0.6.4", "tiny-worker": "^2.1.1", "webpack": "^3.10.0", "webpack-dev-server": "^2.4.1", diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 61b61e51..cab40908 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -1,3 +1,4 @@ +const TextEncoder = require('text-encoding').TextEncoder; const EventEmitter = require('events'); const centralDispatch = require('./dispatch/central-dispatch'); From b02d7e8cd84a633fc1897be5eb4e7464b6babdbf Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Mon, 8 Jan 2018 15:34:58 -0500 Subject: [PATCH 713/721] Read benchmark warm up time and recording time from uri --- src/playground/benchmark.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/playground/benchmark.js b/src/playground/benchmark.js index 3fd1bc2a..843ffca6 100644 --- a/src/playground/benchmark.js +++ b/src/playground/benchmark.js @@ -14,7 +14,7 @@ document.querySelector('.run') }, false); const loadProject = function () { - let id = location.hash.substring(1); + let id = location.hash.substring(1).split(',')[0]; if (id.length < 1 || !isFinite(id)) { id = projectInput.value; } @@ -310,7 +310,7 @@ class ProfilerRun { dom: document.getElementsByClassName('profile-count-group')[0], runningStats, - maxRecordedTime: 6000 + maxRecordedTime }); const frames = this.frames = new Frames(profiler); @@ -388,10 +388,21 @@ window.onload = function () { .innerText = progress.complete; }).on(storage); + let warmUpTime = 4000; + let maxRecordedTime = 6000; + + if (location.hash) { + const split = location.hash.substring(1).split(','); + if (split[1] && split[1].length > 0) { + warmUpTime = Number(split[1]); + } + maxRecordedTime = Number(split[2] || '0') || 6000; + } + new ProfilerRun({ vm, - warmUpTime: 4000, - maxRecordedTime: 6000 + warmUpTime, + maxRecordedTime }).run(); // Instantiate the renderer and connect it to the VM. From c6f4d89371815627ef846b836d19dd7f0cf5a0b6 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Mon, 8 Jan 2018 15:35:43 -0500 Subject: [PATCH 714/721] Add benchmark suite, run and report on multiple benchmark projects --- src/playground/benchmark.js | 16 ++ src/playground/suite.css | 45 +++++ src/playground/suite.html | 23 +++ src/playground/suite.js | 385 ++++++++++++++++++++++++++++++++++++ 4 files changed, 469 insertions(+) create mode 100644 src/playground/suite.css create mode 100644 src/playground/suite.html create mode 100644 src/playground/suite.js diff --git a/src/playground/benchmark.js b/src/playground/benchmark.js index 843ffca6..bca3e053 100644 --- a/src/playground/benchmark.js +++ b/src/playground/benchmark.js @@ -348,11 +348,21 @@ class ProfilerRun { run () { loadProject(); + window.parent.postMessage({ + type: 'BENCH_MESSAGE_LOADING' + }, '*'); + this.vm.on('workspaceUpdate', () => { setTimeout(() => { + window.parent.postMessage({ + type: 'BENCH_MESSAGE_WARMING_UP' + }, '*'); this.vm.greenFlag(); }, 100); setTimeout(() => { + window.parent.postMessage({ + type: 'BENCH_MESSAGE_ACTIVE' + }, '*'); this.vm.runtime.profiler = this.profiler; }, 100 + this.warmUpTime); setTimeout(() => { @@ -362,6 +372,12 @@ class ProfilerRun { this.frameTable.render(); this.opcodeTable.render(); + + window.parent.postMessage({ + type: 'BENCH_MESSAGE_COMPLETE', + frames: this.frames.frames, + opcodes: this.opcodes.opcodes + }, '*'); }, 100 + this.warmUpTime + this.maxRecordedTime); }); } diff --git a/src/playground/suite.css b/src/playground/suite.css new file mode 100644 index 00000000..08eb5d61 --- /dev/null +++ b/src/playground/suite.css @@ -0,0 +1,45 @@ +html, body { + width: 100%; + height: 100%; + margin: 0; + overflow: hidden; +} + +.runner-controls { + position: absolute; + width: 30em; + height: 100%; + left: 0; + right: 30em; + top: 0; + bottom: 0; + overflow: scroll; + padding: 1em; + box-sizing: border-box; +} + +.bench-frame-owner { + position: absolute; + width: calc(100% - 30em); + height: 100%; + left: 30em; + right: 100%; + top: 0; + bottom: 0; +} + +.fixture-project { + display: inline-block; +} + +.fixture-warm-up { + display: inline-block; +} + +.fixture-recording { + display: inline-block; +} + +.result-status { + float: right; +} diff --git a/src/playground/suite.html b/src/playground/suite.html new file mode 100644 index 00000000..8acd7ca6 --- /dev/null +++ b/src/playground/suite.html @@ -0,0 +1,23 @@ + + + + + + Scratch VM Benchmark Suite + + + +
+

Scratch VM Benchmark Suite

+
+ +
+
+
+ +
+ + + + + diff --git a/src/playground/suite.js b/src/playground/suite.js new file mode 100644 index 00000000..11482812 --- /dev/null +++ b/src/playground/suite.js @@ -0,0 +1,385 @@ +const soon = (() => { + let _soon; + return () => { + if (!_soon) { + _soon = Promise.resolve() + .then(() => { + _soon = null; + }); + } + return _soon; + }; +})(); + +class Emitter { + constructor () { + Object.defineProperty(this, '_listeners', { + value: {}, + enumerable: false + }); + } + on (name, listener, context) { + if (!this._listeners[name]) { + this._listeners[name] = []; + } + + this._listeners[name].push(listener, context); + } + off (name, listener, context) { + if (this._listeners[name]) { + if (listener) { + for (let i = 0; i < this._listeners[name].length; i += 2) { + if ( + this._listeners[name][i] === listener && + this._listeners[name][i + 1] === context) { + this._listeners[name].splice(i, 2); + i -= 2; + } + } + } else { + for (let i = 0; i < this._listeners[name].length; i += 2) { + if (this._listeners[name][i + 1] === context) { + this._listeners[name].splice(i, 2); + i -= 2; + } + } + } + } + } + emit (name, ...args) { + if (this._listeners[name]) { + for (let i = 0; i < this._listeners[name].length; i += 2) { + this._listeners[name][i].call(this._listeners[name][i + 1] || this, ...args); + } + } + } +} + +class BenchFrameStream extends Emitter { + constructor (frame) { + super(); + + this.frame = frame; + window.addEventListener('message', message => { + this.emit('message', message.data); + }); + } + + send (message) { + this.frame.send(message); + } +} + +const BENCH_MESSAGE_TYPE = { + LOAD: 'BENCH_MESSAGE_LOAD', + LOADING: 'BENCH_MESSAGE_LOADING', + WARMING_UP: 'BENCH_MESSAGE_WARMING_UP', + ACTIVE: 'BENCH_MESSAGE_ACTIVE', + COMPLETE: 'BENCH_MESSAGE_COMPLETE' +}; + +class BenchUtil { + constructor (frame) { + this.frame = frame; + this.benchStream = new BenchFrameStream(frame); + } + + startBench (args) { + Promise.resolve() + .then(() => new Promise(resolve => setTimeout(resolve, 100))) + .then(() => { + this.frame.contentWindow.location.assign('about:blank'); + }) + .then(() => new Promise(resolve => setTimeout(resolve, 100))) + .then(() => { + this.frame.contentWindow.location.assign( + `/benchmark/#${args.projectId},${args.warmUpTime},${args.recordingTime}`); + }); + } +} + +const BENCH_STATUS = { + INACTIVE: 'BENCH_STATUS_INACTIVE', + STARTING: 'BENCH_STATUS_STARTING', + LOADING: 'BENCH_STATUS_LOADING', + WARMING_UP: 'BENCH_STATUS_WARMING_UP', + ACTIVE: 'BENCH_STATUS_ACTIVE', + COMPLETE: 'BENCH_STATUS_COMPLETE' +}; + +class BenchResult { + constructor ({fixture, status = BENCH_STATUS.INACTIVE, frames = null, opcodes = null}) { + this.fixture = fixture; + this.status = status; + this.frames = frames; + this.opcodes = opcodes; + } +} + +class BenchFixture extends Emitter { + constructor ({ + projectId, + warmUpTime = 4000, + recordingTime = 6000 + }) { + super(); + + this.projectId = projectId; + this.warmUpTime = warmUpTime; + this.recordingTime = recordingTime; + } + + get id () { + return `${this.projectId}-${this.warmUpTime}-${this.recordingTime}`; + } + + run (util) { + return new Promise(resolve => { + util.benchStream.on('message', message => { + const result = { + fixture: this, + status: BENCH_STATUS.STARTING, + frames: null, + opcodes: null + }; + if (message.type === BENCH_MESSAGE_TYPE.LOADING) { + result.status = BENCH_STATUS.LOADING; + } else if (message.type === BENCH_MESSAGE_TYPE.WARMING_UP) { + result.status = BENCH_STATUS.WARMING_UP; + } else if (message.type === BENCH_MESSAGE_TYPE.ACTIVE) { + result.status = BENCH_STATUS.ACTIVE; + } else if (message.type === BENCH_MESSAGE_TYPE.COMPLETE) { + result.status = BENCH_STATUS.COMPLETE; + result.frames = message.frames; + result.opcodes = message.opcodes; + resolve(new BenchResult(result)); + util.benchStream.off('message', null, this); + } + this.emit('result', new BenchResult(result)); + }, this); + util.startBench(this); + }); + } +} + +class BenchSuiteResult extends Emitter { + constructor ({suite, results = []}) { + super(); + + this.suite = suite; + this.results = results; + + if (suite) { + suite.on('result', result => { + if (result.status === BENCH_STATUS.COMPLETE) { + this.results.push(results); + this.emit('add', this); + } + }); + } + } +} + +class BenchSuite extends Emitter { + constructor (fixtures = []) { + super(); + + this.fixtures = fixtures; + } + + add (fixture) { + this.fixtures.push(fixture); + } + + run (util) { + return new Promise(resolve => { + const fixtures = this.fixtures.slice(); + const results = []; + const push = result => { + result.fixture.off('result', null, this); + results.push(result); + }; + const emitResult = this.emit.bind(this, 'result'); + const pop = () => { + const fixture = fixtures.shift(); + if (fixture) { + fixture.on('result', emitResult, this); + fixture.run(util) + .then(push) + .then(pop); + } else { + resolve(new BenchSuiteResult({suite: this, results})); + } + }; + pop(); + }); + } +} + +class BenchRunner extends Emitter { + constructor ({frame, suite}) { + super(); + + this.frame = frame; + this.suite = suite; + this.util = new BenchUtil(frame); + } + + run () { + return this.suite.run(this.util); + } +} + +const viewNames = { + [BENCH_STATUS.INACTIVE]: 'Inactive', + [BENCH_STATUS.STARTING]: 'Starting', + [BENCH_STATUS.LOADING]: 'Loading', + [BENCH_STATUS.WARMING_UP]: 'Warming Up', + [BENCH_STATUS.ACTIVE]: 'Active', + [BENCH_STATUS.COMPLETE]: 'Complete' +}; + +class BenchResultView { + constructor ({result}) { + this.result = result; + this.dom = document.createElement('div'); + } + + update (result) { + soon().then(() => this.render(result)); + } + + render (newResult = this.result) { + const blockFunctionFrame = (newResult.frames ? newResult.frames : []) + .find(frame => frame.name === 'blockFunction'); + const stepThreadsInnerFrame = (newResult.frames ? newResult.frames : []) + .find(frame => frame.name === 'Sequencer.stepThreads#inner'); + + const blocksPerSecond = blockFunctionFrame ? + (blockFunctionFrame.executions / (stepThreadsInnerFrame.totalTime / 1000)) | 0 : + 0; + + this.dom.innerHTML = ` + + (${newResult.fixture.warmUpTime / 1000}s + ${newResult.fixture.recordingTime / 1000}s) +
+ ${blockFunctionFrame ? `${blocksPerSecond} blocks/s` : viewNames[newResult.status]} +
+ `; + + this.result = newResult; + return this; + } +} + +class BenchSuiteResultView { + constructor ({runner}) { + this.runner = runner; + this.suite = runner.suite; + this.views = {}; + this.dom = document.createElement('div'); + + const {suite} = runner; + for (const fixture of suite.fixtures) { + this.views[fixture.id] = new BenchResultView({result: new BenchResult({fixture})}); + this.dom.appendChild(this.views[fixture.id].render().dom); + } + + suite.on('result', result => { + this.views[result.fixture.id].update(result); + }); + } + + render () { + return this; + } +} + +window.onload = function () { + const suite = new BenchSuite(); + + suite.add(new BenchFixture({ + projectId: 130041250, + warmUpTime: 0, + recordingTime: 2000 + })); + + suite.add(new BenchFixture({ + projectId: 130041250, + warmUpTime: 4000, + recordingTime: 6000 + })); + + suite.add(new BenchFixture({ + projectId: 14844969, + warmUpTime: 0, + recordingTime: 2000 + })); + + suite.add(new BenchFixture({ + projectId: 14844969, + warmUpTime: 1000, + recordingTime: 6000 + })); + + suite.add(new BenchFixture({ + projectId: 173918262, + warmUpTime: 0, + recordingTime: 5000 + })); + + suite.add(new BenchFixture({ + projectId: 173918262, + warmUpTime: 5000, + recordingTime: 5000 + })); + + suite.add(new BenchFixture({ + projectId: 155128646, + warmUpTime: 0, + recordingTime: 5000 + })); + + suite.add(new BenchFixture({ + projectId: 155128646, + warmUpTime: 5000, + recordingTime: 5000 + })); + + suite.add(new BenchFixture({ + projectId: 89811578, + warmUpTime: 0, + recordingTime: 5000 + })); + + suite.add(new BenchFixture({ + projectId: 89811578, + warmUpTime: 5000, + recordingTime: 5000 + })); + + suite.add(new BenchFixture({ + projectId: 139193539, + warmUpTime: 0, + recordingTime: 5000 + })); + + suite.add(new BenchFixture({ + projectId: 139193539, + warmUpTime: 5000, + recordingTime: 5000 + })); + + const frame = document.getElementsByTagName('iframe')[0]; + const runner = new BenchRunner({frame, suite}); + const resultsView = new BenchSuiteResultView({runner}); + + document.getElementsByClassName('suite-results')[0].appendChild(resultsView.dom); + + runner.run(); +}; From 479f1ba163c1baf56da7cbf5df58a3eee903e23e Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Tue, 16 Jan 2018 18:20:26 -0500 Subject: [PATCH 715/721] Produce a benchmark share link storing the report in the url --- src/playground/benchmark.css | 26 +++++++++++ src/playground/benchmark.js | 89 ++++++++++++++++++++++++++++++++---- src/playground/index.html | 20 ++++++-- 3 files changed, 122 insertions(+), 13 deletions(-) diff --git a/src/playground/benchmark.css b/src/playground/benchmark.css index 07068f59..746c173a 100644 --- a/src/playground/benchmark.css +++ b/src/playground/benchmark.css @@ -11,16 +11,42 @@ p { left: 450px; } } +.share { + display: none; +} +.share label { + cursor: pointer; +} +.share[href] { + display: inline; +} + +.render .profile-tables { + position: static; + left: 0; +} +.render .description { + display: none; +} +.render .run-push { + display: none; +} #scratch-stage { border: 5px solid black; display: block; width: 400px; height: 300px; } +.render #scratch-stage { + display: none; +} .loading label, .profile-count label{ width: 15em; display: inline-block; } +.render .loading { + display: none; +} .profile-tables table { margin: 30px 0 30px 0px; } diff --git a/src/playground/benchmark.js b/src/playground/benchmark.js index bca3e053..3c296397 100644 --- a/src/playground/benchmark.js +++ b/src/playground/benchmark.js @@ -13,12 +13,20 @@ document.querySelector('.run') location.reload(); }, false); +const setShareLink = function (json) { + document.querySelector('.share') + .href = `#view/${btoa(JSON.stringify(json))}`; + document.querySelectorAll('.share')[1] + .href = `suite.html`; +}; + const loadProject = function () { let id = location.hash.substring(1).split(',')[0]; if (id.length < 1 || !isFinite(id)) { id = projectInput.value; } Scratch.vm.downloadProjectId(id); + return id; }; /** @@ -221,6 +229,13 @@ const frameOrder = [ 'Runtime._step' ]; +const trackSlowFrames = [ + 'Sequencer.stepThreads', + 'Sequencer.stepThreads#inner', + 'Sequencer.stepThread', + 'execute' +]; + class FramesTable extends StatTable { constructor (options) { super(options); @@ -241,12 +256,7 @@ class FramesTable extends StatTable { } isSlow (key, frame) { - return ([ - 'Sequencer.stepThreads', - 'Sequencer.stepThreads#inner', - 'Sequencer.stepThread', - 'execute' - ].indexOf(key) > 0 && + return (trackSlowFrames.indexOf(key) > 0 && frame.selfTime / frame.totalTime > SLOW); } } @@ -346,7 +356,7 @@ class ProfilerRun { } run () { - loadProject(); + this.projectId = loadProject(); window.parent.postMessage({ type: 'BENCH_MESSAGE_LOADING' @@ -378,12 +388,49 @@ class ProfilerRun { frames: this.frames.frames, opcodes: this.opcodes.opcodes }, '*'); + + setShareLink({ + fixture: { + projectId: this.projectId, + warmUpTime: this.warmUpTime, + recordingTime: this.maxRecordedTime + }, + frames: this.frames.frames, + opcodes: this.opcodes.opcodes + }); }, 100 + this.warmUpTime + this.maxRecordedTime); }); } + + render (json) { + const {fixture} = json; + document.querySelector('[type=text]').value = [ + fixture.projectId, + fixture.warmUpTime, + fixture.recordingTime + ].join(','); + + this.frames.frames = json.frames.map( + frame => Object.assign(new StatView(), frame, { + name: this.profiler.nameById(this.profiler.idByName(frame.name)) + }) + ); + + this.opcodes.opcodes = {}; + Object.entries(json.opcodes).forEach(([opcode, data]) => { + this.opcodes.opcodes[opcode] = Object.assign(new StatView(), data); + }); + + this.frameTable.render(); + this.opcodeTable.render(); + } } -window.onload = function () { +/** + * Run the benchmark with given parameters in the location's hash field or + * using defaults. + */ +const runBenchmark = function () { // Lots of global variables to make debugging easier // Instantiate the VM. const vm = new window.VirtualMachine(); @@ -493,3 +540,29 @@ window.onload = function () { // Run threads vm.start(); }; + +/** + * Render previously run benchmark data. + * @param {object} json data from a previous benchmark run. + */ +const renderBenchmarkData = function (json) { + const vm = new window.VirtualMachine(); + new ProfilerRun({vm}).render(json); + setShareLink(json); +}; + +window.onload = function () { + if (location.hash.substring(1).startsWith('view')) { + document.body.className = 'render'; + const data = location.hash.substring(6); + const frozen = atob(data); + const json = JSON.parse(frozen); + renderBenchmarkData(json); + } else { + runBenchmark(); + } +}; + +window.onhashchange = function () { + location.reload(); +}; diff --git a/src/playground/index.html b/src/playground/index.html index 81ba4f57..9a0fcfbe 100644 --- a/src/playground/index.html +++ b/src/playground/index.html @@ -8,19 +8,21 @@

Scratch VM Benchmark

-

+

Welcome to the scratch-vm benchmark. This tool helps you profile a scratch project. When you load the page, it: -

    +
    1. loads the default project and enables turbo mode
    2. runs the project for 4 seconds to warm up
    3. profiles for 6 seconds
    4. stops and reports

    - - -

    +

    + + +
    +

    Try a different project, like `130041250`

    @@ -43,6 +45,14 @@ ...
+ +
From 775d38d8d5b59f3a35835db22643e4dae6cd603e Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Tue, 16 Jan 2018 18:32:19 -0500 Subject: [PATCH 716/721] Truncate benchmark selfTime and totalTime values --- src/playground/benchmark.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/playground/benchmark.js b/src/playground/benchmark.js index 3c296397..4e6d383b 100644 --- a/src/playground/benchmark.js +++ b/src/playground/benchmark.js @@ -134,10 +134,16 @@ class StatView { } cell = document.createElement('td'); + // Truncate selfTime. Value past the microsecond are floating point + // noise. + this.selfTime = Math.floor(this.selfTime * 1000) / 1000; cell.innerText = (this.selfTime / 1000).toPrecision(3); row.appendChild(cell); cell = document.createElement('td'); + // Truncate totalTime. Value past the microsecond are floating point + // noise. + this.totalTime = Math.floor(this.totalTime * 1000) / 1000; cell.innerText = (this.totalTime / 1000).toPrecision(3); row.appendChild(cell); From 4d4966cba94904fd3be901b107e425c3085b9f69 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Tue, 16 Jan 2018 18:33:35 -0500 Subject: [PATCH 717/721] Save and compare results, show individual results in iframe --- src/playground/suite.css | 33 +++++ src/playground/suite.html | 6 +- src/playground/suite.js | 267 ++++++++++++++++++++++++++++++++++---- 3 files changed, 278 insertions(+), 28 deletions(-) diff --git a/src/playground/suite.css b/src/playground/suite.css index 08eb5d61..06df9a3f 100644 --- a/src/playground/suite.css +++ b/src/playground/suite.css @@ -5,6 +5,10 @@ html, body { overflow: hidden; } +iframe { + border: none; +} + .runner-controls { position: absolute; width: 30em; @@ -15,6 +19,7 @@ html, body { bottom: 0; overflow: scroll; padding: 1em; + padding-top: 0; box-sizing: border-box; } @@ -28,8 +33,24 @@ html, body { bottom: 0; } +.controls { + margin-bottom: 1em; +} + +.legend { + margin: 1em 0; +} + +.result-view { + border-bottom: 1px solid #ccc; + border-spacing: 0; + border-collapse: collapse; + padding: 5px; +} + .fixture-project { display: inline-block; + clear: both; } .fixture-warm-up { @@ -40,6 +61,18 @@ html, body { display: inline-block; } +.result-view.resume { + cursor: pointer; +} + .result-status { float: right; + text-align: right; + margin-left: 0.3em; +} + +.compare-file { + cursor: pointer; + visibility: hidden; + width: 0; } diff --git a/src/playground/suite.html b/src/playground/suite.html index 8acd7ca6..4100ddd8 100644 --- a/src/playground/suite.html +++ b/src/playground/suite.html @@ -10,11 +10,13 @@

Scratch VM Benchmark Suite

- +
+
- +
diff --git a/src/playground/suite.js b/src/playground/suite.js index 11482812..8ab0d492 100644 --- a/src/playground/suite.js +++ b/src/playground/suite.js @@ -70,7 +70,16 @@ class BenchFrameStream extends Emitter { } } +const benchmarkUrlArgs = args => ( + [ + args.projectId, + args.warmUpTime, + args.recordingTime + ].join(',') +); + const BENCH_MESSAGE_TYPE = { + INACTIVE: 'BENCH_MESSAGE_INACTIVE', LOAD: 'BENCH_MESSAGE_LOAD', LOADING: 'BENCH_MESSAGE_LOADING', WARMING_UP: 'BENCH_MESSAGE_WARMING_UP', @@ -84,22 +93,38 @@ class BenchUtil { this.benchStream = new BenchFrameStream(frame); } + setFrameLocation (url) { + this.frame.contentWindow.location.assign(url); + } + startBench (args) { - Promise.resolve() - .then(() => new Promise(resolve => setTimeout(resolve, 100))) + this.benchArgs = args; + this.setFrameLocation(`index.html#${benchmarkUrlArgs(args)}`); + } + + pauseBench () { + new Promise(resolve => setTimeout(resolve, 1000)) .then(() => { - this.frame.contentWindow.location.assign('about:blank'); - }) - .then(() => new Promise(resolve => setTimeout(resolve, 100))) - .then(() => { - this.frame.contentWindow.location.assign( - `/benchmark/#${args.projectId},${args.warmUpTime},${args.recordingTime}`); + this.benchStream.emit('message', { + type: BENCH_MESSAGE_TYPE.INACTIVE + }); }); } + + resumeBench () { + this.startBench(this.benchArgs); + } + + renderResults (results) { + this.setFrameLocation( + `index.html#view/${btoa(JSON.stringify(results))}` + ); + } } const BENCH_STATUS = { INACTIVE: 'BENCH_STATUS_INACTIVE', + RESUME: 'BENCH_STATUS_RESUME', STARTING: 'BENCH_STATUS_STARTING', LOADING: 'BENCH_STATUS_LOADING', WARMING_UP: 'BENCH_STATUS_WARMING_UP', @@ -142,7 +167,9 @@ class BenchFixture extends Emitter { frames: null, opcodes: null }; - if (message.type === BENCH_MESSAGE_TYPE.LOADING) { + if (message.type === BENCH_MESSAGE_TYPE.INACTIVE) { + result.status = BENCH_STATUS.RESUME; + } else if (message.type === BENCH_MESSAGE_TYPE.LOADING) { result.status = BENCH_STATUS.LOADING; } else if (message.type === BENCH_MESSAGE_TYPE.WARMING_UP) { result.status = BENCH_STATUS.WARMING_UP; @@ -232,6 +259,7 @@ class BenchRunner extends Emitter { const viewNames = { [BENCH_STATUS.INACTIVE]: 'Inactive', + [BENCH_STATUS.RESUME]: 'Resume', [BENCH_STATUS.STARTING]: 'Starting', [BENCH_STATUS.LOADING]: 'Loading', [BENCH_STATUS.WARMING_UP]: 'Warming Up', @@ -240,8 +268,10 @@ const viewNames = { }; class BenchResultView { - constructor ({result}) { + constructor ({result, benchUtil}) { this.result = result; + this.compare = null; + this.benchUtil = benchUtil; this.dom = document.createElement('div'); } @@ -249,26 +279,114 @@ class BenchResultView { soon().then(() => this.render(result)); } - render (newResult = this.result) { - const blockFunctionFrame = (newResult.frames ? newResult.frames : []) + resume () { + this.benchUtil.resumeBench(); + } + + setFrameLocation (loc) { + this.benchUtil.pauseBench(); + this.benchUtil.setFrameLocation(loc); + } + + act (ev) { + if ( + ev.type === 'click' && + ev.button === 0 && + !(ev.altKey || ev.ctrlKey || ev.shiftKey || ev.metaKey) + ) { + let target = ev.target; + while (target && target.tagName.toLowerCase() !== 'a') { + target = target.parentElement; + } + if (target && target.tagName.toLowerCase() === 'a') { + if (target.href) { + this.setFrameLocation(target.href); + ev.preventDefault(); + } + } else if (ev.currentTarget.classList.contains('resume')) { + this.resume(); + } + } + } + + render (newResult = this.result, compareResult = this.compare) { + const newResultFrames = (newResult.frames ? newResult.frames : []); + const blockFunctionFrame = newResultFrames .find(frame => frame.name === 'blockFunction'); - const stepThreadsInnerFrame = (newResult.frames ? newResult.frames : []) + const stepThreadsInnerFrame = newResultFrames .find(frame => frame.name === 'Sequencer.stepThreads#inner'); const blocksPerSecond = blockFunctionFrame ? - (blockFunctionFrame.executions / (stepThreadsInnerFrame.totalTime / 1000)) | 0 : + (blockFunctionFrame.executions / + (stepThreadsInnerFrame.totalTime / 1000)) | 0 : + 0; + const stepsPerSecond = stepThreadsInnerFrame ? + (stepThreadsInnerFrame.executions / + (stepThreadsInnerFrame.totalTime / 1000)) | 0 : 0; + const compareResultFrames = ( + compareResult && compareResult.frames ? + compareResult.frames : + [] + ); + const blockFunctionCompareFrame = compareResultFrames + .find(frame => frame.name === 'blockFunction'); + const stepThreadsInnerCompareFrame = compareResultFrames + .find(frame => frame.name === 'Sequencer.stepThreads#inner'); + + const compareBlocksPerSecond = blockFunctionCompareFrame ? + (blockFunctionCompareFrame.executions / + (stepThreadsInnerCompareFrame.totalTime / 1000)) | 0 : + 0; + const compareStepsPerSecond = stepThreadsInnerCompareFrame ? + (stepThreadsInnerCompareFrame.executions / + (stepThreadsInnerCompareFrame.totalTime / 1000)) | 0 : + 0; + + const statusName = viewNames[newResult.status]; + + this.dom.className = `result-view ${ + viewNames[newResult.status].toLowerCase() + }`; + this.dom.onclick = this.act.bind(this); + let url = `index.html#${benchmarkUrlArgs(newResult.fixture)}`; + if (newResult.status === BENCH_STATUS.COMPLETE) { + url = `index.html#view/${btoa(JSON.stringify(newResult))}`; + } + let compareUrl = url; + if (compareResult && compareResult) { + compareUrl = + `index.html#view/${btoa(JSON.stringify(compareResult))}`; + } + let compareHTML = ''; + if (stepThreadsInnerFrame && stepThreadsInnerCompareFrame) { + compareHTML = ` +
+
${compareStepsPerSecond}
+
${compareBlocksPerSecond}
+
+
`; + } this.dom.innerHTML = ` - (${newResult.fixture.warmUpTime / 1000}s - ${newResult.fixture.recordingTime / 1000}s)
- ${blockFunctionFrame ? `${blocksPerSecond} blocks/s` : viewNames[newResult.status]} +
${stepThreadsInnerFrame ? `steps/s` : ''}
+
${blockFunctionFrame ? `blocks/s` : statusName}
+
+ +
+
${stepThreadsInnerFrame ? `${stepsPerSecond}` : ''}
+
${blockFunctionFrame ? `${blocksPerSecond}` : ''}
+
+
+ ${compareHTML} +
+ Run for ${newResult.fixture.recordingTime / 1000} seconds after + ${newResult.fixture.warmUpTime / 1000} seconds
`; @@ -279,15 +397,18 @@ class BenchResultView { class BenchSuiteResultView { constructor ({runner}) { + const {suite, util} = runner; + this.runner = runner; - this.suite = runner.suite; + this.suite = suite; this.views = {}; this.dom = document.createElement('div'); - const {suite} = runner; for (const fixture of suite.fixtures) { - this.views[fixture.id] = new BenchResultView({result: new BenchResult({fixture})}); - this.dom.appendChild(this.views[fixture.id].render().dom); + this.views[fixture.id] = new BenchResultView({ + result: new BenchResult({fixture}), + benchUtil: util + }); } suite.on('result', result => { @@ -296,12 +417,93 @@ class BenchSuiteResultView { } render () { + this.dom.innerHTML = `
+ Project ID +
+
steps per second
+
blocks per second
+
+
Description
+
+ + `; + + for (const fixture of this.suite.fixtures) { + this.dom.appendChild(this.views[fixture.id].render().dom); + } + return this; } } +let suite; +let suiteView; + +window.upload = function (_this) { + if (!_this.files.length) { + return; + } + const reader = new FileReader(); + reader.onload = function () { + const report = JSON.parse(reader.result); + Object.values(suiteView.views) + .forEach(view => { + const sameFixture = report.results.find(result => ( + result.fixture.projectId === + view.result.fixture.projectId && + result.fixture.warmUpTime === + view.result.fixture.warmUpTime && + result.fixture.recordingTime === + view.result.fixture.recordingTime + )); + + if (sameFixture) { + if ( + view.result && view.result.frames && + view.result.frames.length > 0 + ) { + view.render(view.result, sameFixture); + } else { + view.compare = sameFixture; + } + } + }); + }; + reader.readAsText(_this.files[0]); +}; + +window.download = function (_this) { + const blob = new Blob([JSON.stringify({ + meta: { + source: 'Scratch VM Benchmark Suite', + version: 1 + }, + results: Object.values(suiteView.views) + .map(view => view.result) + .filter(view => view.status === BENCH_STATUS.COMPLETE) + })], {type: 'application/json'}); + + _this.download = 'scratch-vm-benchmark.json'; + _this.href = URL.createObjectURL(blob); +}; + window.onload = function () { - const suite = new BenchSuite(); + suite = new BenchSuite(); suite.add(new BenchFixture({ projectId: 130041250, @@ -375,11 +577,24 @@ window.onload = function () { recordingTime: 5000 })); + suite.add(new BenchFixture({ + projectId: 187694931, + warmUpTime: 0, + recordingTime: 5000 + })); + + suite.add(new BenchFixture({ + projectId: 187694931, + warmUpTime: 5000, + recordingTime: 5000 + })); + const frame = document.getElementsByTagName('iframe')[0]; const runner = new BenchRunner({frame, suite}); - const resultsView = new BenchSuiteResultView({runner}); + const resultsView = suiteView = new BenchSuiteResultView({runner}).render(); - document.getElementsByClassName('suite-results')[0].appendChild(resultsView.dom); + document.getElementsByClassName('suite-results')[0] + .appendChild(resultsView.dom); runner.run(); }; From 4778ab8d4f00c2dc439c5c7980261bd237e247ec Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 22 Jan 2018 10:15:41 -0500 Subject: [PATCH 718/721] Fix music extension asset loading on safari --- src/extensions/scratch3_music/index.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index 0e14a215..bbec66a4 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -97,10 +97,23 @@ class Scratch3MusicBlocks { _loadSound (fileName, index, bufferArray) { if (!this.runtime.storage) return; if (!this.runtime.audioEngine) return; + if (!this.runtime.audioEngine.audioContext) return; return this.runtime.storage.load(this.runtime.storage.AssetType.Sound, fileName, 'mp3') - .then(soundAsset => - this.runtime.audioEngine.audioContext.decodeAudioData(soundAsset.data.buffer) - ) + .then(soundAsset => { + const context = this.runtime.audioEngine.audioContext; + // Check for newer promise-based API + if (context.decodeAudioData.length === 1) { + return context.decodeAudioData(soundAsset.data.buffer); + } else { // eslint-disable no-else-return + // Fall back to callback API + return new Promise((resolve, reject) => + context.decodeAudioData(soundAsset.data.buffer, + buffer => resolve(buffer), + error => reject(error) + ) + ); + } + }) .then(buffer => { bufferArray[index] = buffer; }); From 9eaf67b50411bb7c895e714226bb943e01e08164 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 22 Jan 2018 10:27:16 -0500 Subject: [PATCH 719/721] Fix eslint-disable-line --- src/extensions/scratch3_music/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index bbec66a4..6202b2ce 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -104,7 +104,7 @@ class Scratch3MusicBlocks { // Check for newer promise-based API if (context.decodeAudioData.length === 1) { return context.decodeAudioData(soundAsset.data.buffer); - } else { // eslint-disable no-else-return + } else { // eslint-disable-line no-else-return // Fall back to callback API return new Promise((resolve, reject) => context.decodeAudioData(soundAsset.data.buffer, From 073ee84a91a47ee4265ab48a1deada7141391c3e Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 22 Jan 2018 11:57:06 -0500 Subject: [PATCH 720/721] Keep bufferSource refs so onended fires on safari --- src/extensions/scratch3_music/index.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/extensions/scratch3_music/index.js b/src/extensions/scratch3_music/index.js index 6202b2ce..6d391bb9 100644 --- a/src/extensions/scratch3_music/index.js +++ b/src/extensions/scratch3_music/index.js @@ -60,6 +60,15 @@ class Scratch3MusicBlocks { */ this._instrumentBufferArrays = []; + /** + * An array of audio bufferSourceNodes. Each time you play an instrument or drum sound, + * a bufferSourceNode is created. We keep references to them to make sure their onended + * events can fire. + * @type {Array} + * @private + */ + this._bufferSources = []; + this._loadAllSounds(); } @@ -555,9 +564,14 @@ class Scratch3MusicBlocks { bufferSource.buffer = this._drumBuffers[drumNum]; bufferSource.connect(outputNode); bufferSource.start(); + + const bufferSourceIndex = this._bufferSources.length; + this._bufferSources.push(bufferSource); + this._concurrencyCounter++; bufferSource.onended = () => { this._concurrencyCounter--; + delete this._bufferSources[bufferSourceIndex]; }; } @@ -638,6 +652,10 @@ class Scratch3MusicBlocks { // Create the audio buffer to play the note, and set its pitch const context = util.runtime.audioEngine.audioContext; const bufferSource = context.createBufferSource(); + + const bufferSourceIndex = this._bufferSources.length; + this._bufferSources.push(bufferSource); + bufferSource.buffer = this._instrumentBufferArrays[inst][sampleIndex]; const sampleNote = sampleArray[sampleIndex]; bufferSource.playbackRate.value = this._ratioForPitchInterval(note - sampleNote); @@ -667,6 +685,7 @@ class Scratch3MusicBlocks { this._concurrencyCounter++; bufferSource.onended = () => { this._concurrencyCounter--; + delete this._bufferSources[bufferSourceIndex]; }; } From 275b0c7944cb514f9c52685bb0abf7fc1e3a73a2 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 22 Jan 2018 16:38:45 -0500 Subject: [PATCH 721/721] =?UTF-8?q?Get=20customState=20but=20don=E2=80=99t?= =?UTF-8?q?=20create=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/blocks/scratch3_sound.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 1d7cfa23..f79575b2 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -102,7 +102,7 @@ class Scratch3SoundBlocks { */ _onTargetCreated (newTarget, sourceTarget) { if (sourceTarget) { - const soundState = this._getSoundState(sourceTarget); + const soundState = sourceTarget.getCustomState(Scratch3SoundBlocks.STATE_KEY); if (soundState && newTarget) { newTarget.setCustomState(Scratch3SoundBlocks.STATE_KEY, Clone.simple(soundState)); this._syncEffectsForTarget(newTarget);