Merge branch 'LLK/master'

This commit is contained in:
Scimonster 2013-11-03 18:01:50 +02:00
commit ba40cd3396
24 changed files with 764 additions and 698 deletions

View file

@ -1,9 +1,16 @@
Scratch HTML5 Player
# Scratch HTML5 Player
This project aims to create a Scratch Player in HTML5. Scratch is currently implemented with Actionscript 3 and requires the Flash Player version 10.2. Since Flash does not run on iOS (iPads, iPods, etc) and newer Android devices, we would like to have an HTML5 version to display (but not edit) projects on mobile devices. Scratch projects played in the HTML5 player should look and behave as closely as possible to the way they look and behave when played by the Flash player. We will not be accepting pull requests for new features that don't already exist in the Flash based Scratch project player.
There are a few github issues created that represent some of the missing features. At this point, the HTML5 player is about 40% complete and can run some simple projects. Running the HTML5 player on your own website, or locally, you will need to have PHP so that the proxy.php file can be used to load assets from the same domain. This is done to be compatible with Javascript security models in today's browsers. To test the HTML5 player against the Flash player you can use the compare.html web page.
There are a few github issues created that represent some of the missing features. At this point, the HTML5 player is about 40% complete and can run some simple projects.
Unimplementable Features on iOS: Image effects for whirl, fisheye, mosaic, and pixelate. Sound and video input for loudness, video motion, and touching colors from the video.
More documentation will be added as time permits. Thanks for contributing, and Scratch On!
## Installation
Running the HTML5 player on your own website, or locally, you will need to have
PHP so that the `proxy.php` file can be used to load assets from the same domain. This is done to be compatible with Javascript security models in today's browsers. To test the HTML5 player against the Flash player you can use the compare.html web page.
See the file `TESTING.md` for more details.

45
TESTING.md Normal file
View file

@ -0,0 +1,45 @@
Running the HTML5 player on your own website, or locally, you will need to have
PHP so that the `proxy.php` file can be used to load assets from the same domain. This is done to be compatible with Javascript security models in today's browsers. To test the HTML5 player against the Flash player you can use the compare.html web page.
# Ubuntu
If you're using Ubuntu, you can follow the following steps to set up the proxy correctly. You'll need to type the following commands in Terminal.
Install PHP and Apache for running the proxy file:
$ sudo apt-get install apache2 php5
Fork this repository on Github, and then clone it into your home folder somewhere (replacing `<username>` with your Github username):
$ git clone https://github.com/<username>/scratch-html5
We'd like to add a new localhost domain, so that we can access the player from our web browser. Something like `scratch.localhost`. Run this command to edit the `/etc/hosts` file:
$ sudo nano /etc/hosts
Add the following line:
127.0.0.1 scratch.localhost
Use <key>Ctrl-O</key>, <key>Return</key>, <key>Ctrl-X</key> to quit.
Now we want to add a new Apache configuration for the new domain. Run the following:
$ cd /etc/apache2/sites-available/
$ sudo nano scratch
And type in the following, replacing `user` with your username, and making sure the `DocumentRoot` matches where you cloned the repository earlier:
<VirtualHost *:80>
ServerName scratch.localhost
DocumentRoot /home/user/scratch-html5
</VirtualHost>
Finally, run the following commands to enable the site:
$ sudo a2ensite scratch
$ sudo service apache2 reload
Now when you go to <http://scratch.localhost/>, the project should play. If you get a "Forbidden" error message, you may need to check the permissions of the folders that the player code is in.
Now you can go fix bugs in the HTML5 player!

View file

@ -4,112 +4,113 @@ header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
?>
<!DOCTYPE html>
<html>
<head>
<title>Scratch HTML5 vs. Flash</title>
<meta charset="utf-8">
<style type="text/css">
body {
background: #222;
color: #fff;
margin: 0;
}
</style>
<link href="player.css" type="text/css" rel="stylesheet" />
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
<script src="js/util/Timer.js"></script>
<script src="js/util/OffsetBuffer.js"></script>
<script src="js/util/Color.js"></script>
<script src="js/util/Rectangle.js"></script>
<script src="js/Sprite.js"></script>
<script src="js/Reporter.js"></script>
<script src="js/Stage.js"></script>
<script src="js/sound/WAVFile.js"></script>
<script src="js/sound/SoundDecoder.js"></script>
<script src="js/sound/SoundBank.js"></script>
<script src="js/sound/NotePlayer.js"></script>
<script src="soundbank/Instr.js"></script>
<script src="js/IO.js"></script>
<script src="js/primitives/VarListPrims.js"></script>
<script src="js/primitives/MotionAndPenPrims.js"></script>
<script src="js/primitives/LooksPrims.js"></script>
<script src="js/primitives/SensingPrims.js"></script>
<script src="js/primitives/SoundPrims.js"></script>
<script src="js/primitives/Primitives.js"></script>
<script src="js/Interpreter.js"></script>
<script src="js/Runtime.js"></script>
<script src="js/Scratch.js"></script>
<script type="text/javascript">
if (window.location.hash) {
var project_id = parseInt(window.location.hash.substr(1));
} else {
var project_id = 10000160; // Default project for display
}
</script>
<script>
$(function() {
// The flashvars tell flash about the project data (and autostart=true)
var flashvars = {
server: encodeURIComponent('scratch.mit.edu'),
project_id: project_id
};
<head>
<title>Scratch HTML5 vs. Flash</title>
<meta charset="utf-8">
<style type="text/css">
body {
background: #222;
color: #fff;
margin: 0;
}
</style>
<link href="player.css" type="text/css" rel="stylesheet" />
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
<script src="js/util/Timer.js"></script>
<script src="js/util/OffsetBuffer.js"></script>
<script src="js/util/Color.js"></script>
<script src="js/util/Rectangle.js"></script>
<script src="js/Sprite.js"></script>
<script src="js/Reporter.js"></script>
<script src="js/Stage.js"></script>
<script src="js/sound/WAVFile.js"></script>
<script src="js/sound/SoundDecoder.js"></script>
<script src="js/sound/SoundBank.js"></script>
<script src="js/sound/NotePlayer.js"></script>
<script src="soundbank/Instr.js"></script>
<script src="js/IO.js"></script>
<script src="js/primitives/VarListPrims.js"></script>
<script src="js/primitives/MotionAndPenPrims.js"></script>
<script src="js/primitives/LooksPrims.js"></script>
<script src="js/primitives/SensingPrims.js"></script>
<script src="js/primitives/SoundPrims.js"></script>
<script src="js/primitives/Primitives.js"></script>
<script src="js/Interpreter.js"></script>
<script src="js/Runtime.js"></script>
<script src="js/Scratch.js"></script>
<script type="text/javascript">
if (window.location.hash) {
var project_id = parseInt(window.location.hash.substr(1));
} else {
var project_id = 10000160; // Default project for display
}
</script>
<script>
$(function() {
// The flashvars tell flash about the project data (and autostart=true)
var flashvars = {
server: encodeURIComponent('scratch.mit.edu'),
project_id: project_id
};
// Pass in the cloud token for the project
if(window.getCloudToken)
flashvars.cloud_token = encodeURIComponent(getCloudToken());
// Pass in the cloud token for the project
if (window.getCloudToken) {
flashvars.cloud_token = encodeURIComponent(getCloudToken());
}
var params = {
allowscriptaccess: 'always',
allowfullscreen: 'false',
wmode: 'direct',
menu: 'false'};
var params = {
allowscriptaccess: 'always',
allowfullscreen: 'false',
wmode: 'direct',
menu: 'false'
};
var flashPlayer = null;
//var swf_url = "http://cdn.scratch.mit.edu/scratchr2/static/Scratch.swf";
var swf_url = "http://jiggler.media.mit.edu/shanemc/scratchr2/static/Scratch.swf";
swfobject.embedSWF(swf_url, "flashScratch", "480", "400", "10.2.0",
"http://cdn.scratch.mit.edu/scratchr2/static/expressInstall.swf",
flashvars, params, null, function(e) {
$('#flashScratch').css('visibility', 'visible');
if(e.success) flashPlayer = e.ref;
var flashPlayer = null;
//var swf_url = "http://cdn.scratch.mit.edu/scratchr2/static/Scratch.swf";
var swf_url = "http://jiggler.media.mit.edu/shanemc/scratchr2/static/Scratch.swf";
swfobject.embedSWF(swf_url, "flashScratch", "480", "400", "10.2.0",
"http://cdn.scratch.mit.edu/scratchr2/static/expressInstall.swf",
flashvars, params, null, function(e) {
$('#flashScratch').css('visibility', 'visible');
if (e.success) flashPlayer = e.ref;
});
$('#trigger_green_flag, #greenSlide').click(function() {
flashPlayer.ASstartRunning();
});
// Stop button behavior
$('#trigger_stop').click(function() {
flashPlayer.ASstopRunning();
});
});
$('#trigger_green_flag, #greenSlide').click(function() {
flashPlayer.ASstartRunning();
});
// Stop button behavior
$('#trigger_stop').click(function() {
flashPlayer.ASstopRunning();
});
});
</script>
</head>
<body>
<div style="display: inline-block;">
<div id="up">&nbsp;</div>
<div id="left">&nbsp;</div>
<div id="overContainer">
<div id="greenSlide"><div id="greenSlideFg"><img src="img/greenflag.png"></div></div>
<div id="container"></div>
</script>
</head>
<body>
<div style="display: inline-block;">
<div id="up">&nbsp;</div>
<div id="left">&nbsp;</div>
<div id="overContainer">
<div id="greenSlide"><div id="greenSlideFg"><img src="img/greenflag.png"></div></div>
<div id="container"></div>
</div>
<div id="right">&nbsp;</div>
<div id="down">&nbsp;</div>
</div>
<div id="right">&nbsp;</div>
<div id="down">&nbsp;</div>
</div>
<div style="display: inline-block;">
<div id="flashScratch" style="text-align:center;visibility:hidden;">
<p style="color:#aaa;font-size:22px;margin-top:14px;line-height:28px;">Oh Noes! Scratch project cannot display.<br/>Flash player is disabled, missing, or less than version 10.2.</p>
<a href="http://www.adobe.com/go/getflashplayer">
<img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" target="_blank" />
</a>
<div style="display: inline-block;">
<div id="flashScratch" style="text-align:center;visibility:hidden;">
<p style="color:#aaa;font-size:22px;margin-top:14px;line-height:28px;">Oh Noes! Scratch project cannot display.<br/>Flash player is disabled, missing, or less than version 10.2.</p>
<a href="http://www.adobe.com/go/getflashplayer">
<img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" target="_blank" />
</a>
</div>
</div>
</div>
<div id="info">Loading...</div>
<div id="info">Loading...</div>
<button id="trigger_green_flag">Green flag</button>
<button id="trigger_stop">Stop</button>
<button id="trigger_green_flag">Green flag</button>
<button id="trigger_stop">Stop</button>
<input type="text" name="project_id" id="project_id" /><button id='go_project'>Go!</button>
</body>
<input type="text" name="project_id" id="project_id" /><button id='go_project'>Go!</button>
</body>
</html>

View file

@ -27,26 +27,24 @@ var IO = function() {
// In production, simply use the local path (no proxy)
// since we won't be hampered by the same-origin policy.
this.base = 'proxy.php?resource=internalapi/';
this.project_base = this.base + 'project/';
this.project_base = this.base + 'project/';
this.project_suffix = '/get/';
this.asset_base = this.base + 'asset/';
this.asset_suffix = '/get/';
this.soundbank_base = 'soundbank/';
this.spriteLayerCount = 0;
}
};
IO.prototype.loadProject = function(project_id) {
var runningIO = this;
$.getJSON(this.project_base + project_id + this.project_suffix,
function(data) {
runningIO.data = data;
runningIO.makeObjects();
runningIO.loadThreads();
runningIO.loadNotesDrums();
runtime.loadStart(); // Try to run the project.
}
);
}
$.getJSON(this.project_base + project_id + this.project_suffix, function(data) {
runningIO.data = data;
runningIO.makeObjects();
runningIO.loadThreads();
runningIO.loadNotesDrums();
runtime.loadStart(); // Try to run the project.
});
};
IO.prototype.soundRequest = function(sound, sprite) {
var request = new XMLHttpRequest();
@ -63,9 +61,9 @@ IO.prototype.soundRequest = function(sound, sprite) {
data[i] = samples[i];
}
sprite.soundsLoaded++;
}
};
request.send();
}
};
IO.prototype.loadNotesDrums = function() {
var self = this;
@ -81,18 +79,17 @@ IO.prototype.loadNotesDrums = function() {
var soundBuffer = waveData.readBytes(2 * info.sampleCount);
Instr.samples[name] = soundBuffer;
Instr.wavsLoaded++;
}
};
request.send();
});
}
};
IO.prototype.makeObjects = function() {
// Create the stage
runtime.stage = new Stage(this.data);
runtime.stage.attach(runtime.scene);
runtime.stage.attachPenLayer(runtime.scene);
runtime.stage.loadSounds();
// Create the sprites and watchers
$.each(this.data.children.concat(this.data.lists), function i(index, obj) {
var newSprite;
@ -111,8 +108,8 @@ IO.prototype.makeObjects = function() {
if (!obj.cmd && !obj.listName)
newSprite.loadSounds();
});
}
};
IO.prototype.loadThreads = function() {
var target = runtime.stage;
var scripts = target.data.scripts;
@ -127,9 +124,9 @@ IO.prototype.loadThreads = function() {
$.each(target.data.scripts, function(j, s) {
target.stacks.push(interp.makeBlockList(s[2]));
});
}
}
});
}
};
// Returns the number sprite we are rendering
// used for initial layering assignment
@ -137,4 +134,4 @@ IO.prototype.getCount = function() {
var rv = this.spriteLayerCount;
this.spriteLayerCount++;
return rv;
}
};

View file

@ -22,7 +22,7 @@
var Block = function(opAndArgs, optionalSubstack) {
this.op = opAndArgs[0];
this.primFcn = interp.lookupPrim(this.op);
this.primFcn = interp.lookupPrim(this.op);
this.args = opAndArgs.slice(1); // arguments can be either or constants (numbers, boolean strings, etc.) or expressions (Blocks)
this.isLoop = false; // set to true for loop blocks the first time they run
this.substack = optionalSubstack;
@ -30,7 +30,7 @@ var Block = function(opAndArgs, optionalSubstack) {
this.nextBlock = null;
this.tmp = -1;
interp.fixArgs(this);
}
};
var Thread = function(block, target) {
this.nextBlock = block; // next block to run; null when thread is finished
@ -40,7 +40,7 @@ var Thread = function(block, target) {
this.tmp = null; // used for thread operations like Timer
this.tmpObj = []; // used for Sprite operations like glide
this.firstTime = true;
}
};
var Interpreter = function() {
// Interpreter state
@ -54,10 +54,10 @@ var Interpreter = function() {
this.yield = false;
this.doRedraw = false;
this.opCount = 0; // used to benchmark the interpreter
}
};
// Utilities for building blocks and sequences of blocks
Interpreter.prototype.fixArgs = function(b) {
Interpreter.prototype.fixArgs = function(b) {
// Convert the arguments of the given block into blocks or substacks if necessary.
// A block argument can be a constant (numbers, boolean strings, etc.), an expression (Blocks), or a substack (an array of blocks).
var newArgs = [];
@ -66,7 +66,7 @@ Interpreter.prototype.fixArgs = function(b) {
if (arg && arg.constructor == Array) {
if ((arg.length > 0) && (arg[0].constructor == Array)) {
// if first element arg is itself an array, then arg is a substack
if(!b.substack) {
if (!b.substack) {
b.substack = this.makeBlockList(arg);
} else {
b.substack2 = this.makeBlockList(arg);
@ -80,7 +80,7 @@ Interpreter.prototype.fixArgs = function(b) {
}
}
b.args = newArgs;
}
};
Interpreter.prototype.makeBlockList = function(blockList) {
var firstBlock = null, lastBlock = null;
@ -91,7 +91,7 @@ Interpreter.prototype.makeBlockList = function(blockList) {
lastBlock = b;
}
return firstBlock;
}
};
// The Interpreter proper
Interpreter.prototype.stepThreads = function() {
@ -115,17 +115,17 @@ Interpreter.prototype.stepThreads = function() {
if (this.threads[a].nextBlock != null) {
newThreads.push(this.threads[a]);
}
}
}
this.threads = newThreads;
if (this.threads.length == 0) return;
}
this.currentMSecs = this.timer.time();
}
}
};
Interpreter.prototype.stepActiveThread = function() {
// Run the active thread until it yields.
if(typeof(this.activeThread) == 'undefined') {
if (typeof(this.activeThread) == 'undefined') {
return;
}
var b = this.activeThread.nextBlock;
@ -141,7 +141,7 @@ Interpreter.prototype.stepActiveThread = function() {
b = this.activeThread.nextBlock; // refresh local variable b in case primitive did some control flow
while (b == null) {
// end of a substack; pop the owning control flow block from stack
// Note: This is a loop to handle nested control flow blocks.
// Note: This is a loop to handle nested control flow blocks.
b = this.activeThread.stack.pop();
if ((b == null) || (b.isLoop)) {
this.activeThread.nextBlock = b;
@ -149,8 +149,8 @@ Interpreter.prototype.stepActiveThread = function() {
}
}
}
}
};
Interpreter.prototype.toggleThread = function(b, targetObj) {
var newThreads = [], wasRunning = false;
for (var i = 0; i < this.threads.length; i++) {
@ -161,16 +161,16 @@ Interpreter.prototype.toggleThread = function(b, targetObj) {
}
}
this.threads = newThreads;
if(!wasRunning) {
if (!wasRunning) {
this.startThread(b, targetObj);
}
}
}
Interpreter.prototype.startThread = function(b, targetObj) {
this.activeThread = new Thread(b, targetObj);
this.threads.push(this.activeThread);
}
};
Interpreter.prototype.restartThread = function(b, targetObj) {
// used by broadcast; stop any thread running on b, then start a new thread on b
var newThread = new Thread(b, targetObj);
@ -184,7 +184,7 @@ Interpreter.prototype.restartThread = function(b, targetObj) {
if (!wasRunning) {
this.threads.push(newThread);
}
}
};
Interpreter.prototype.arg = function(block, index) {
var arg = block.args[index];
@ -193,11 +193,11 @@ Interpreter.prototype.arg = function(block, index) {
return arg.primFcn(arg); // expression
}
return arg;
}
};
Interpreter.prototype.targetSprite = function() {
return this.activeThread.target;
}
};
// Timer
Interpreter.prototype.startTimer = function(secs) {
@ -206,7 +206,7 @@ Interpreter.prototype.startTimer = function(secs) {
this.activeThread.tmp = this.currentMSecs + waitMSecs; // end time in milliseconds
this.activeThread.firstTime = false;
this.yield = true;
}
};
Interpreter.prototype.checkTimer = function() {
// check for timer expiration and clean up if expired. return true when expired
@ -219,11 +219,11 @@ Interpreter.prototype.checkTimer = function() {
this.yield = true;
return false;
}
}
};
Interpreter.prototype.redraw = function() {
this.doRedraw = true;
}
};
// Primitive operations
Interpreter.prototype.initPrims = function() {
@ -231,14 +231,14 @@ Interpreter.prototype.initPrims = function() {
this.primitiveTable['whenGreenFlag'] = this.primNoop;
this.primitiveTable['whenKeyPressed'] = this.primNoop;
this.primitiveTable['whenClicked'] = this.primNoop;
this.primitiveTable['if'] = function(b) { if (interp.arg(b, 0)) interp.startSubstack(b) };
this.primitiveTable['doForever'] = function(b) { interp.startSubstack(b, true) };
this.primitiveTable['if'] = function(b) { if (interp.arg(b, 0)) interp.startSubstack(b); };
this.primitiveTable['doForever'] = function(b) { interp.startSubstack(b, true); };
this.primitiveTable['doForeverIf'] = function(b) { if (interp.arg(b, 0)) interp.startSubstack(b, true); else interp.yield = true; };
this.primitiveTable['doIf'] = function(b) { if (interp.arg(b, 0)) interp.startSubstack(b); };
this.primitiveTable['doRepeat'] = this.primRepeat;
this.primitiveTable['doIfElse'] = function(b) { if (interp.arg(b, 0)) interp.startSubstack(b); else interp.startSubstack(b, false, true); };
this.primitiveTable['doWaitUntil'] = function(b) { if (!interp.arg(b, 0)) interp.yield = true };
this.primitiveTable['doUntil'] = function(b) { if (!interp.arg(b, 0)) interp.startSubstack(b, true) };
this.primitiveTable['doWaitUntil'] = function(b) { if (!interp.arg(b, 0)) interp.yield = true; };
this.primitiveTable['doUntil'] = function(b) { if (!interp.arg(b, 0)) interp.startSubstack(b, true); };
this.primitiveTable['doReturn'] = function(b) { interp.activeThread = new Thread(null); };
this.primitiveTable['stopAll'] = function(b) { interp.activeThread = new Thread(null); interp.threads = []; }
this.primitiveTable['whenIReceive'] = this.primNoop;
@ -247,27 +247,27 @@ Interpreter.prototype.initPrims = function() {
this.primitiveTable['wait:elapsed:from:'] = this.primWait;
// added by John:
this.primitiveTable['showBubble'] = function(b) { console.log(interp.arg(b, 1)) }
this.primitiveTable['timerReset'] = function(b) {interp.timerBase = (new Date()).getTime() }
this.primitiveTable['timer'] = function(b) {return ((new Date()).getTime() - interp.timerBase) / 1000 }
this.primitiveTable['showBubble'] = function(b) { console.log(interp.arg(b, 1)); };
this.primitiveTable['timerReset'] = function(b) { interp.timerBase = Date.now(); };
this.primitiveTable['timer'] = function(b) { return (Date.now() - interp.timerBase) / 1000; };
new Primitives().addPrimsTo(this.primitiveTable);
}
};
Interpreter.prototype.timerBase = (new Date()).getTime();
Interpreter.prototype.timerBase = Date.now();
Interpreter.prototype.lookupPrim = function(op) {
var fcn = interp.primitiveTable[op];
if (fcn == null) fcn = function(b) { console.log('not implemented: ' + b.op) }
if (fcn == null) fcn = function(b) { console.log('not implemented: ' + b.op); };
return fcn;
}
};
Interpreter.prototype.primNoop = function(b) { console.log(b.op); };
Interpreter.prototype.primNoop = function(b) { console.log(b.op); }
Interpreter.prototype.primWait = function(b) {
if (interp.activeThread.firstTime) interp.startTimer(interp.arg(b, 0));
else interp.checkTimer();
}
};
Interpreter.prototype.primRepeat = function(b) {
if (b.tmp == -1) {
b.tmp = Math.max(interp.arg(b, 0), 0); // Initialize repeat count on this block
@ -280,14 +280,14 @@ Interpreter.prototype.primRepeat = function(b) {
b.tmp = -1;
b = null;
}
}
};
Interpreter.prototype.broadcast = function(b, waitFlag) {
var pair;
if (interp.activeThread.firstTime) {
var receivers = [];
var msg = String(interp.arg(b, 0)).toLowerCase();
var findReceivers = function (stack, target) {
var findReceivers = function(stack, target) {
if ((stack.op == "whenIReceive") && (stack.args[0].toLowerCase() == msg)) {
receivers.push([stack, target]);
}
@ -303,14 +303,14 @@ Interpreter.prototype.broadcast = function(b, waitFlag) {
if (interp.isRunning(interp.activeThread.tmpObj[pair][0])) {
done = false;
}
}
}
if (done) {
interp.activeThread.tmpObj = null;
interp.activeThread.firstTime = true;
} else {
interp.yield = true;
}
}
};
Interpreter.prototype.isRunning = function(b) {
for (t in interp.threads) {
@ -319,7 +319,7 @@ Interpreter.prototype.isRunning = function(b) {
}
}
return false;
}
};
Interpreter.prototype.startSubstack = function(b, isLoop, secondSubstack) {
// Start the substack of a control structure command such as if or forever.
@ -327,9 +327,9 @@ Interpreter.prototype.startSubstack = function(b, isLoop, secondSubstack) {
b.isLoop = true;
this.activeThread.stack.push(b); // remember the block that started the substack
}
if(!secondSubstack) {
if (!secondSubstack) {
this.activeThread.nextBlock = b.substack;
} else {
this.activeThread.nextBlock = b.substack2;
}
}
};

View file

@ -33,7 +33,7 @@ var Reporter = function(data) {
this.el = null; // jQuery Element for the outer box
this.valueEl = null; // jQ element containing the reporter value
this.slider = null; // slider jQ element
}
};
Reporter.prototype.attach = function(scene) {
switch (this.mode) {
@ -47,7 +47,7 @@ Reporter.prototype.attach = function(scene) {
// Temporarily, set the value to sliderMin until an update
this.slider = $('<input type="range" min="' + this.sliderMin +
'" max="' + this.sliderMax + '" step="1" value="' +
this.sliderMin + '" data-target="' + this.target +
this.sliderMin + '" data-target="' + this.target +
'" data-var="' + this.param + '">');
this.slider.change(this.changeSlider);
this.el.append('<br>');
@ -68,13 +68,13 @@ Reporter.prototype.attach = function(scene) {
this.valueEl.css('background-color', 'rgb(' + cR + ',' + cG + ',' + cB + ')');
this.el.css('display', this.visible ? 'inline-block' : 'none');
scene.append(this.el);
}
};
Reporter.prototype.update = function() {
this.el.css('display', this.visible ? 'inline-block' : 'none');
if (!this.visible) return;
var newValue ='';
var newValue = '';
var target = runtime.spriteNamed(this.target);
switch (this.cmd) {
case 'getVar:':
@ -104,20 +104,21 @@ Reporter.prototype.update = function() {
break;
}
this.valueEl.html(newValue);
if (this.mode == 3)
if (this.mode == 3) {
this.slider.val(parseInt(newValue));
}
}
};
Reporter.prototype.updateLayer = function() {
this.el.css('z-index', this.z);
}
};
Reporter.prototype.changeSlider = function() {
var newValue = parseInt($(this).val());
var target = runtime.spriteNamed($(this).attr('data-target'));
var variable = $(this).attr('data-var');
target.variables[variable] = newValue;
}
};
var List = function(data) {
this.contents = data.contents;
@ -171,7 +172,7 @@ List.prototype.attach = function(scene) {
this.el.height(this.height);
this.el.css('z-index', this.z);
this.el.css('display', this.visible ? 'inline-block' : 'none');
}
};
List.prototype.update = function(){
this.el.css('display', this.visible ? 'inline-block' : 'none');
@ -191,4 +192,4 @@ List.prototype.update = function(){
List.prototype.updateLayer = function() {
this.el.css('z-index', this.z);
}
};

View file

@ -35,8 +35,8 @@ var Runtime = function() {
this.audioPlaying = [];
this.notesPlaying = [];
this.projectLoaded = false;
}
};
// Initializer for the drawing and audio contexts.
Runtime.prototype.init = function() {
this.scene = $('#container');
@ -44,8 +44,8 @@ Runtime.prototype.init = function() {
this.audioContext = new AudioContext();
this.audioGain = this.audioContext.createGainNode();
this.audioGain.connect(runtime.audioContext.destination);
}
};
// Load start waits for the stage and the sprites to be loaded, without
// hanging the browser. When the loading is finished, we begin the step
// and animate methods.
@ -69,7 +69,7 @@ Runtime.prototype.loadStart = function() {
$('#info').html("Loaded!");
setInterval(this.step, 33);
this.projectLoaded = true;
}
};
Runtime.prototype.greenFlag = function() {
if (this.projectLoaded) {
@ -78,7 +78,7 @@ Runtime.prototype.greenFlag = function() {
interp.primitiveTable.timerReset();
this.startGreenFlags();
}
}
};
Runtime.prototype.stopAll = function() {
interp.activeThread = new Thread(null);
@ -86,10 +86,11 @@ Runtime.prototype.stopAll = function() {
stopAllSounds();
// Hide reporters
for (var s = 0; s < runtime.sprites.length; s++) {
if (typeof runtime.sprites[s].hideBubble == 'function')
if (typeof runtime.sprites[s].hideBubble == 'function') {
runtime.sprites[s].hideBubble();
}
}
}
};
// Step method for execution - called every 33 milliseconds
Runtime.prototype.step = function() {
@ -97,8 +98,8 @@ Runtime.prototype.step = function() {
for (var r = 0; r < runtime.reporters.length; r++) {
runtime.reporters[r].update();
}
}
};
// Stack functions -- push and remove stacks
// to be run by the interpreter as threads.
Runtime.prototype.allStacksDo = function(f) {
@ -106,25 +107,25 @@ Runtime.prototype.allStacksDo = function(f) {
var stack;
for (var i = runtime.sprites.length-1; i >= 0; i--) {
var o = runtime.sprites[i];
if(typeof(o) == 'object' && o.constructor == Sprite) {
if (typeof(o) == 'object' && o.constructor == Sprite) {
$.each(o.stacks, function(index, stack) {
f(stack, o);
});
}
}
$.each(stage.stacks, function(index, stack) {
f(stack, stage);
f(stack, stage);
});
}
};
// Hat triggers
Runtime.prototype.startGreenFlags = function() {
function startIfGreenFlag(stack, target) {
if (stack.op == 'whenGreenFlag') interp.toggleThread(stack, target);
}
this.allStacksDo(startIfGreenFlag);
}
};
Runtime.prototype.startKeyHats = function(ch) {
var keyName = null;
if (('A'.charCodeAt(0) <= ch) && (ch <= 'Z'.charCodeAt(0)) ||
@ -140,44 +141,59 @@ Runtime.prototype.startKeyHats = function(ch) {
if (ch == 32) keyName = "space";
if (keyName == null) return;
var startMatchingKeyHats = function (stack, target) {
var startMatchingKeyHats = function(stack, target) {
if ((stack.op == "whenKeyPressed") && (stack.args[0] == keyName)) {
// Only start the stack if it is not already running
if (!interp.isRunning(stack))
if (!interp.isRunning(stack)) {
interp.toggleThread(stack, target);
}
}
}
runtime.allStacksDo(startMatchingKeyHats);
}
};
Runtime.prototype.startClickedHats = function(sprite) {
function startIfClicked(stack, target) {
if(target == sprite && stack.op == "whenClicked") {
if(!interp.isRunning(stack))
interp.toggleThread(stack, target);
if (target == sprite && stack.op == "whenClicked" && !interp.isRunning(stack)) {
interp.toggleThread(stack, target);
}
}
runtime.allStacksDo(startIfClicked);
}
};
// Returns true if a key is pressed.
Runtime.prototype.keyIsDown = function(ch) {
return this.keysDown[ch] || false;
}
};
// Sprite named -- returns one of the sprites on the stage.
Runtime.prototype.spriteNamed = function(n) {
if (n == 'Stage') return this.stage;
var selected_sprite = null;
$.each(this.sprites, function(index, s) {
if (s.objName == n) {
selected_sprite = s;
selected_sprite = s;
return false;
}
});
return selected_sprite;
}
};
Runtime.prototype.getTimeString = function(which) {
// Return local time properties.
var now = new Date();
switch (which) {
case 'hour': return now.getHours();
case 'minute': return now.getMinutes();
case 'second': return now.getSeconds();
case 'year': return now.getFullYear(); // four digit year (e.g. 2012)
case 'month': return now.getMonth() + 1; // 1-12
case 'date': return now.getDate(); // 1-31
case 'day of week': return now.getDay() + 1; // 1-7, where 1 is Sunday
}
return ''; // shouldn't happen
};
// Reassigns z-indices for layer functions
Runtime.prototype.reassignZ = function(target, move) {
var sprites = this.sprites;
@ -189,18 +205,18 @@ Runtime.prototype.reassignZ = function(target, move) {
sprites.splice(index, 1);
}
});
if (move == null) {
// Move to the front
this.sprites.splice(this.sprites.length, 0, target);
} else if (oldIndex - move >= 0 && oldIndex - move < this.sprites.length+1) {
} else if (oldIndex - move >= 0 && oldIndex - move < this.sprites.length + 1) {
// Move to the new position
this.sprites.splice(oldIndex - move, 0, target);
} else {
// No change is required
this.sprites.splice(oldIndex, 0, target);
}
}
// Renumber the z-indices
var newZ = 1;
$.each(this.sprites, function(index, sprite) {
@ -208,4 +224,4 @@ Runtime.prototype.reassignZ = function(target, move) {
sprite.updateLayer();
newZ++;
});
}
};

View file

@ -23,58 +23,58 @@
'use strict';
var runtime, interp, io, iosAudioActive = false;
$(function () {
$(function() {
runtime = new Runtime();
runtime.init();
$(window).keydown(function(e) {
runtime.keysDown[e.which] = true;
runtime.startKeyHats(e.which);
});
$(window).keyup(function(e) {
delete runtime.keysDown[e.which];
});
// Update the project ID field
$('#project_id').val(project_id);
// Go project button behavior
$('#go_project').click(function() {
window.location = "#" + parseInt($('#project_id').val());
window.location.reload(true);
});
// Green flag behavior
$('#trigger_green_flag, #greenSlide').click(function() {
$('#greenSlide').css('display', 'none');
runtime.greenFlag()
});
// Stop button behavior
$('#trigger_stop').click(function() {
runtime.stopAll();
});
// Canvas container mouse events
$('#container').mousedown(function(e) {
runtime.mouseDown = true;
//e.preventDefault();
});
$('#container').mouseup(function(e) {
runtime.mouseDown = false;
//e.preventDefault();
});
$('#container').mousemove(function(e) {
var absX = e.pageX - this.offsetLeft;
var absY = e.pageY - this.offsetTop;
runtime.mousePos = [absX-240, -absY+180];
});
// Touch events - EXPERIMENTAL
$(window).bind('touchstart', function(e) {
$(window).bind('touchstart', function(e) {
// On iOS, we need to activate the Web Audio API
// with an empty sound play on the first touch event.
if (!iosAudioActive) {
@ -85,8 +85,8 @@ $(function () {
isource.noteOn(0);
iosAudioActive = true;
}
});
});
$('#container').bind('touchstart', function(e) {
runtime.mouseDown = true;
});
@ -94,14 +94,14 @@ $(function () {
$('#container').bind('touchend', function(e) {
runtime.mouseDown = true;
});
$('#container').bind('touchmove', function(e) {
var touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
var absX = touch.pageX - this.offsetLeft;
var absY = touch.pageY - this.offsetTop;
runtime.mousePos = [absX-240, -absY+180];
});
// Border touch events - EXPERIMENTAL
$('#left').bind('touchstart touchmove', function(e) { runtime.keysDown[37] = true; runtime.startKeyHats(37); });
$('#left').bind('touchend', function(e) { delete runtime.keysDown[37]; });
@ -111,11 +111,11 @@ $(function () {
$('#right').bind('touchend', function(e) { delete runtime.keysDown[39]; });
$('#down').bind('touchstart touchmove', function(e) { runtime.keysDown[40] = true; runtime.startKeyHats(40); });
$('#down').bind('touchend', function(e) { delete runtime.keysDown[40]; });
// Load the interpreter and primitives
interp = new Interpreter();
interp.initPrims();
// Load the requested project and go!
io = new IO();
io.loadProject(project_id);

View file

@ -22,12 +22,12 @@
'use strict';
var Sprite = function(data) {
if(!this.data) {
if (!this.data) {
this.data = data;
}
// Public variables used for Scratch-accessible data.
this.visible = (typeof(this.data.visible) == "undefined") ? true : data.visible;
this.visible = typeof(this.data.visible) == "undefined" ? true : data.visible;
this.scratchX = data.scratchX || 0;
this.scratchY = data.scratchY || 0;
@ -94,8 +94,8 @@ var Sprite = function(data) {
// Stacks to be pushed to the interpreter and run
this.stacks = [];
}
};
// Attaches a Sprite (<img>) to a Scratch scene
Sprite.prototype.attach = function(scene) {
// Create textures and materials for each of the costumes.
@ -109,7 +109,7 @@ Sprite.prototype.attach = function(scene) {
sprite.costumesLoaded += 1;
sprite.updateCostume();
$(sprite.textures[c]).css('display', (sprite.currentCostumeIndex == c) ? 'inline' : 'none');
$(sprite.textures[c]).css('display', sprite.currentCostumeIndex == c ? 'inline' : 'none');
$(sprite.textures[c]).css('position', 'absolute').css('left', '0px').css('top', '0px');
$(sprite.textures[c]).bind('dragstart', function(evt) { evt.preventDefault(); })
.bind('selectstart', function(evt) { evt.preventDefault(); })
@ -137,24 +137,23 @@ Sprite.prototype.attach = function(scene) {
this.talkBubbleStyler = $('<div class="bubble-say"></div>');
this.talkBubble.append(this.talkBubbleBox);
this.talkBubble.append(this.talkBubbleStyler);
runtime.scene.append(this.talkBubble);
}
};
// Load sounds from the server and buffer them
Sprite.prototype.loadSounds = function() {
var spr = this;
$.each(this.sounds, function (index, sound) {
$.each(this.sounds, function(index, sound) {
io.soundRequest(sound, spr);
});
}
};
// True when all the costumes have been loaded
Sprite.prototype.isLoaded = function() {
return (this.costumesLoaded == this.costumes.length
&& this.soundsLoaded == Object.keys(this.sounds).length);
}
return this.costumesLoaded == this.costumes.length && this.soundsLoaded == Object.keys(this.sounds).length;
};
// Step methods
Sprite.prototype.showCostume = function(costume) {
if (costume < 0) {
@ -167,34 +166,34 @@ Sprite.prototype.showCostume = function(costume) {
this.currentCostumeIndex = costume;
}
this.updateCostume();
}
};
Sprite.prototype.indexOfCostumeNamed = function(name) {
for(var i in this.costumes) {
for (var i in this.costumes) {
var c = this.costumes[i];
if(c['costumeName'] == name) {
if (c['costumeName'] == name) {
return i;
}
}
return null;
}
};
Sprite.prototype.showCostumeNamed = function(name) {
var index = this.indexOfCostumeNamed(name);
if(!index) return;
if (!index) return;
this.showCostume(index);
}
};
Sprite.prototype.updateCostume = function() {
if(!this.textures[this.currentCostumeIndex]) {
if (!this.textures[this.currentCostumeIndex]) {
this.currentCostumeIndex = 0;
}
$(this.mesh).css('display', 'none');
this.mesh = this.textures[this.currentCostumeIndex];
this.updateVisible();
this.updateTransform();
}
};
Sprite.prototype.onClick = function(evt) {
// TODO - needs work!!
var boxOffset = $('#container').offset();
@ -238,34 +237,35 @@ Sprite.prototype.onClick = function(evt) {
$(underElement).click();
$(this.mesh).show();
}
}
};
Sprite.prototype.setVisible = function(v) {
this.visible = v;
this.updateVisible();
}
};
Sprite.prototype.updateLayer = function() {
$(this.mesh).css('z-index', this.z);
if (this.talkBubble) this.talkBubble.css('z-index', this.z);
}
};
Sprite.prototype.updateVisible = function() {
$(this.mesh).css('display', (this.visible) ? 'inline' : 'none');
if (this.talkBubbleOn)
this.talkBubble.css('display', (this.visible) ? 'inline-block' : 'none');
}
$(this.mesh).css('display', this.visible ? 'inline' : 'none');
if (this.talkBubbleOn) {
this.talkBubble.css('display', this.visible ? 'inline-block' : 'none');
}
};
Sprite.prototype.updateTransform = function() {
var texture = this.textures[this.currentCostumeIndex];
var resolution = this.costumes[this.currentCostumeIndex].bitmapResolution || 1;
var drawWidth = texture.width * this.scale / resolution;
var drawHeight = texture.height * this.scale / resolution;
var rotationCenterX = this.costumes[this.currentCostumeIndex].rotationCenterX;
var rotationCenterY = this.costumes[this.currentCostumeIndex].rotationCenterY;
var drawX = this.scratchX + (480 / 2) - rotationCenterX;
var drawY = -this.scratchY + (360 / 2) - rotationCenterY;
@ -275,26 +275,26 @@ Sprite.prototype.updateTransform = function() {
// sign to the X scale.
}
$(this.mesh).css('transform',
$(this.mesh).css('transform',
'translatex(' + drawX + 'px) \
translatey(' + drawY + 'px) \
rotate(' + this.rotation + 'deg) \
scaleX(' + scaleXprepend + (this.scale / resolution) + ') scaleY(' + (this.scale / resolution) + ')');
$(this.mesh).css('-moz-transform',
$(this.mesh).css('-moz-transform',
'translatex(' + drawX + 'px) \
translatey(' + drawY + 'px) \
rotate(' + this.rotation + 'deg) \
scaleX(' + scaleXprepend + this.scale + ') scaleY(' + this.scale / resolution + ')');
$(this.mesh).css('-webkit-transform',
$(this.mesh).css('-webkit-transform',
'translatex(' + drawX + 'px) \
translatey(' + drawY + 'px) \
rotate(' + this.rotation + 'deg) \
scaleX(' + scaleXprepend + (this.scale / resolution) + ') scaleY(' + (this.scale / resolution) + ')');
$(this.mesh).css('-webkit-transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
$(this.mesh).css('-moz-transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
$(this.mesh).css('-ms-transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
$(this.mesh).css('-o-transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
$(this.mesh).css('-webkit-transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
$(this.mesh).css('-moz-transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
$(this.mesh).css('-ms-transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
$(this.mesh).css('-o-transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
$(this.mesh).css('transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
// Don't forget to update the talk bubble.
@ -303,9 +303,9 @@ Sprite.prototype.updateTransform = function() {
this.talkBubble.css('left', xy[0] + 'px');
this.talkBubble.css('top', xy[1] + 'px');
}
this.updateLayer();
}
this.updateLayer();
};
Sprite.prototype.getTalkBubbleXY = function() {
var texture = this.textures[this.currentCostumeIndex];
@ -316,7 +316,7 @@ Sprite.prototype.getTalkBubbleXY = function() {
var drawX = this.scratchX + (480 / 2) - rotationCenterX;
var drawY = -this.scratchY + (360 / 2) - rotationCenterY;
return [drawX + drawWidth, drawY - drawHeight / 2];
}
};
Sprite.prototype.showBubble = function(text, type) {
var xy = this.getTalkBubbleXY();
@ -329,30 +329,34 @@ Sprite.prototype.showBubble = function(text, type) {
this.talkBubbleStyler.removeClass('bubble-say');
this.talkBubbleStyler.removeClass('bubble-think');
if (type == 'say') this.talkBubbleStyler.addClass('bubble-say');
else if (type == 'think') this.talkBubbleStyler.addClass('bubble-think');
if (this.visible)
if (type == 'say') {
this.talkBubbleStyler.addClass('bubble-say');
} else if (type == 'think') {
this.talkBubbleStyler.addClass('bubble-think');
}
if (this.visible) {
this.talkBubble.css('display', 'inline-block');
}
this.talkBubbleBox.html(text);
}
};
Sprite.prototype.hideBubble = function() {
this.talkBubbleOn = false;
this.talkBubble.css('display', 'none');
}
};
Sprite.prototype.setXY = function(x, y) {
this.scratchX = x;
this.scratchY = y;
this.updateTransform();
}
};
Sprite.prototype.setDirection = function(d) {
var rotation;
d = d % 360
if (d < 0) d += 360;
this.direction = (d > 180) ? d - 360 : d;
this.direction = d > 180 ? d - 360 : d;
if (this.rotationStyle == 'normal') {
rotation = (this.direction - 90) % 360;
} else if (this.rotationStyle == 'leftRight') {
@ -367,23 +371,23 @@ Sprite.prototype.setDirection = function(d) {
}
this.rotation = rotation;
this.updateTransform();
}
};
Sprite.prototype.setRotationStyle = function(r) {
this.rotationStyle = r;
}
};
Sprite.prototype.getSize = function() {
return this.scale * 100;
}
};
Sprite.prototype.setSize = function(percent) {
var newScale = percent / 100.0;
newScale = Math.max(0.05, Math.min(newScale, 100));
this.scale = newScale;
this.updateTransform();
}
};
// Move functions
Sprite.prototype.keepOnStage = function() {
var x = this.scratchX + 240;
@ -398,7 +402,7 @@ Sprite.prototype.keepOnStage = function() {
if (myBox.top > edgeBox.bottom) y -= myBox.top - edgeBox.bottom;
this.scratchX = x - 240;
this.scratchY = 180 - y;
}
};
Sprite.prototype.getRect = function() {
var cImg = this.textures[this.currentCostumeIndex];
@ -406,35 +410,38 @@ Sprite.prototype.getRect = function() {
var y = 180 - this.scratchY - (cImg.height/2.0);
var myBox = new Rectangle(x, y, cImg.width, cImg.height);
return myBox;
}
// Pen functions
};
// Pen functions
Sprite.prototype.setPenColor = function(c) {
var hsv = Color.rgb2hsv(c);
this.penHue = (200 * hsv[0]) / 360 ;
this.penShade = 50 * hsv[2]; // not quite right; doesn't account for saturation
this.penColorCache = c;
}
Sprite.prototype.setPenHue = function(n) {
};
Sprite.prototype.setPenHue = function(n) {
this.penHue = n % 200;
if (this.penHue < 0) this.penHue += 200;
this.updateCachedPenColor();
}
Sprite.prototype.setPenShade = function(n) {
};
Sprite.prototype.setPenShade = function(n) {
this.penShade = n % 200;
if (this.penShade < 0) this.penShade += 200;
this.updateCachedPenColor();
}
};
Sprite.prototype.updateCachedPenColor = function() {
var c = Color.fromHSV((this.penHue * 180.0) / 100.0, 1, 1);
var shade = (this.penShade > 100) ? 200 - this.penShade : this.penShade; // range 0..100
var shade = this.penShade > 100 ? 200 - this.penShade : this.penShade; // range 0..100
if (shade < 50) {
this.penColorCache = Color.mixRGB(0, c, (10 + shade) / 60.0);
} else {
this.penColorCache = Color.mixRGB(c, 0xFFFFFF, (shade - 50) / 60);
}
}
};
Sprite.prototype.stamp = function(canvas, opacity) {
var drawWidth = this.textures[this.currentCostumeIndex].width * this.scale;
var drawHeight = this.textures[this.currentCostumeIndex].height * this.scale;
@ -447,12 +454,13 @@ Sprite.prototype.stamp = function(canvas, opacity) {
canvas.drawImage(this.mesh, -drawWidth/2, -drawHeight/2, drawWidth, drawHeight);
canvas.restore();
canvas.globalAlpha = 1;
}
};
Sprite.prototype.soundNamed = function(name) {
if (name in this.sounds && this.sounds[name].buffer)
if (name in this.sounds && this.sounds[name].buffer) {
return this.sounds[name];
else if (name in runtime.stage.sounds && runtime.stage.sounds[name].buffer)
} else if (name in runtime.stage.sounds && runtime.stage.sounds[name].buffer) {
return runtime.stage.sounds[name];
}
return null;
}
};

View file

@ -35,31 +35,28 @@ var Stage = function(data) {
this.lineCache = this.lineCanvas.getContext('2d');
Sprite.call(this, data);
}
};
Stage.prototype = Object.create(Sprite.prototype);
Stage.prototype.constructor = Stage;
Stage.prototype.attachPenLayer = function(scene) {
if (this.penLayerLoaded)
return;
if (this.penLayerLoaded) return;
this.penLayerLoaded = true;
$(this.lineCanvas).css('position', 'absolute');
$(this.lineCanvas).css('z-index', '-1');
scene.append(this.lineCanvas);
}
};
Stage.prototype.isLoaded = function() {
return (this.penLayerLoaded
&& this.costumesLoaded == this.costumes.length
&& this.soundsLoaded == Object.keys(this.sounds).length);
}
return this.penLayerLoaded && this.costumesLoaded == this.costumes.length && this.soundsLoaded == Object.keys(this.sounds).length;
};
// Pen functions
Stage.prototype.clearPenStrokes = function() {
this.lineCache.clearRect(0, 0, 480, 360);
}
};
Stage.prototype.stroke = function(from, to, width, color) {
this.lineCache.lineWidth = width;
this.lineCache.lineCap = 'round';
@ -69,4 +66,4 @@ Stage.prototype.stroke = function(from, to, width, color) {
this.lineCache.lineTo(to[0] + 240.5, 180.5 - to[1]);
this.lineCache.strokeStyle = 'rgb(' + (color >> 16) + ',' + (color >> 8 & 255) + ',' + (color & 255) + ')';
this.lineCache.stroke();
}
};

View file

@ -15,7 +15,7 @@
'use strict';
var LooksPrims = function() {}
var LooksPrims = function() {};
LooksPrims.prototype.addPrimsTo = function(primTable) {
primTable["show"] = this.primShow;
@ -42,28 +42,28 @@ LooksPrims.prototype.addPrimsTo = function(primTable) {
primTable["changeGraphicEffect:by:"] = this.primChangeEffect;
primTable["setGraphicEffect:to:"] = this.primSetEffect;
primTable["filterReset"] = this.primClearEffects;
primTable["say:"] = function(b) { showBubble(b, 'say'); };
primTable["say:duration:elapsed:from:"] = function(b) { showBubbleAndWait(b, 'say'); };
primTable["think:"] = function(b) { showBubble(b, 'think'); };
primTable["think:duration:elapsed:from:"] = function(b) { showBubbleAndWait(b, 'think'); };
}
};
LooksPrims.prototype.primShow = function(b) {
interp.targetSprite().setVisible(true);
interp.redraw();
}
};
LooksPrims.prototype.primHide = function(b) {
interp.targetSprite().setVisible(false);
interp.targetSprite().setVisible(false);
interp.redraw();
}
};
LooksPrims.prototype.primNextCostume = function(b) {
interp.targetSprite().showCostume(interp.targetSprite().currentCostumeIndex + 1);
interp.redraw();
}
};
LooksPrims.prototype.primShowCostume = function(b) {
var s = interp.targetSprite();
if (s == null) return;
@ -88,8 +88,8 @@ LooksPrims.prototype.primShowCostume = function(b) {
}
}
if (s.visible) interp.redraw();
}
};
LooksPrims.prototype.primStartScene = function(b) {
var s = runtime.stage;
var arg = interp.arg(b, 0);
@ -113,55 +113,55 @@ LooksPrims.prototype.primStartScene = function(b) {
}
}
if (s.visible) interp.redraw();
}
};
LooksPrims.prototype.primCostumeNum = function(b) {
var s = interp.targetSprite();
return (s == null) ? 1 : s.currentCostumeIndex + 1;
}
return s == null ? 1 : s.currentCostumeIndex + 1;
};
LooksPrims.prototype.primChangeSize = function(b) {
var s = interp.targetSprite();
if (s == null) return;
s.setSize(s.getSize() + interp.arg(b, 0));
if (s.visible) interp.redraw();
}
};
LooksPrims.prototype.primSetSize = function(b) {
var s = interp.targetSprite();
if (s == null) return;
s.setSize(interp.arg(b, 0));
if (s.visible) interp.redraw();
}
};
LooksPrims.prototype.primSize = function(b) {
var s = interp.targetSprite();
if (s == null) return 100;
return s.getSize();
}
};
LooksPrims.prototype.primGoFront = function(b) {
var s = interp.targetSprite();
runtime.reassignZ(s, null);
if(s.visible) interp.redraw();
}
if (s.visible) interp.redraw();
};
LooksPrims.prototype.primGoBack = function(b) {
var s = interp.targetSprite();
runtime.reassignZ(s, interp.arg(b, 0));
if(s.visible) interp.redraw();
}
LooksPrims.prototype.primChangeEffect = function(b) {}
if (s.visible) interp.redraw();
};
LooksPrims.prototype.primSetEffect = function(b) {}
LooksPrims.prototype.primChangeEffect = function(b) {};
LooksPrims.prototype.primClearEffects = function(b) {}
LooksPrims.prototype.primSetEffect = function(b) {};
LooksPrims.prototype.primClearEffects = function(b) {};
var showBubble = function(b, type) {
var s = interp.targetSprite();
if (s != null) s.showBubble(interp.arg(b, 0), type);
}
};
var showBubbleAndWait = function(b, type) {
var s = interp.targetSprite();
@ -175,4 +175,4 @@ var showBubbleAndWait = function(b, type) {
} else {
if (interp.checkTimer()) s.hideBubble();
}
}
};

View file

@ -15,7 +15,7 @@
'use strict';
var MotionAndPenPrims = function() {}
var MotionAndPenPrims = function() {};
MotionAndPenPrims.prototype.addPrimsTo = function(primTable) {
primTable['forward:'] = this.primMove;
@ -26,19 +26,19 @@ MotionAndPenPrims.prototype.addPrimsTo = function(primTable) {
primTable["gotoX:y:"] = this.primGoTo;
primTable["gotoSpriteOrMouse:"] = this.primGoToSpriteOrMouse;
primTable["glideSecs:toX:y:elapsed:from:"] = this.primGlide;
primTable["changeXposBy:"] = this.primChangeX;
primTable["xpos:"] = this.primSetX;
primTable["changeYposBy:"] = this.primChangeY;
primTable["ypos:"] = this.primSetY;
primTable["bounceOffEdge"] = this.primBounceOffEdge;
primTable["setRotationStyle"] = this.primSetRotationStyle;
primTable["xpos"] = this.primXPosition;
primTable["ypos"] = this.primYPosition;
primTable["heading"] = this.primDirection;
primTable["clearPenTrails"] = this.primClear;
primTable["putPenDown"] = this.primPenDown;
primTable["putPenUp"] = this.primPenUp;
@ -49,63 +49,62 @@ MotionAndPenPrims.prototype.addPrimsTo = function(primTable) {
primTable["changePenShadeBy:"] = this.primChangePenShade;
primTable["penSize:"] = this.primSetPenSize;
primTable["changePenSizeBy:"] = this.primChangePenSize;
primTable["stampCostume"] = this.primStamp;
primTable["stampTransparent"] = this.primStampTransparent;
}
};
MotionAndPenPrims.prototype.primMove = function(b) {
var s = interp.targetSprite();
var radians = ((Math.PI * (90 - s.direction)) / 180);
var radians = (90 - s.direction) * Math.PI / 180;
var d = interp.arg(b, 0);
moveSpriteTo(s, s.scratchX + (d * Math.cos(radians)),
s.scratchY + (d * Math.sin(radians)));
if(s.visible) interp.redraw();
}
moveSpriteTo(s, s.scratchX + d * Math.cos(radians), s.scratchY + d * Math.sin(radians));
if (s.visible) interp.redraw();
};
MotionAndPenPrims.prototype.primTurnLeft = function(b) {
var s = interp.targetSprite();
var d = s.direction - interp.arg(b, 0);
s.setDirection(d);
if(s.visible) interp.redraw();
}
if (s.visible) interp.redraw();
};
MotionAndPenPrims.prototype.primTurnRight = function(b) {
var s = interp.targetSprite();
var d = s.direction + interp.arg(b, 0);
s.setDirection(d);
if(s.visible) interp.redraw();
}
if (s.visible) interp.redraw();
};
MotionAndPenPrims.prototype.primSetDirection = function(b) {
var s = interp.targetSprite();
s.setDirection(interp.arg(b, 0));
if(s.visible) interp.redraw();
}
if (s.visible) interp.redraw();
};
MotionAndPenPrims.prototype.primPointTowards = function(b) {
var s = interp.targetSprite();
var p = mouseOrSpritePosition(interp.arg(b, 0));
if ((s == null) || (p == null)) return;
if (s == null || p == null) return;
var dx = p.x - s.scratchX;
var dy = p.y - s.scratchY;
var angle = 90 - ((Math.atan2(dy, dx) * 180) / Math.PI);
var angle = 90 - Math.atan2(dy, dx) * 180 / Math.PI;
s.setDirection(angle);
if (s.visible) interp.redraw();
}
};
MotionAndPenPrims.prototype.primGoTo = function(b) {
var s = interp.targetSprite();
if (s != null) moveSpriteTo(s, interp.arg(b, 0), interp.arg(b, 1));
}
};
MotionAndPenPrims.prototype.primGoToSpriteOrMouse = function(b) {
var s = interp.targetSprite();
var p = mouseOrSpritePosition(interp.arg(b, 0));
if ((s == null) || (p == null)) return;
if (s == null || p == null) return;
moveSpriteTo(s, p.x, p.y);
}
};
MotionAndPenPrims.prototype.primGlide = function(b) {
var s = interp.targetSprite();
@ -119,44 +118,43 @@ MotionAndPenPrims.prototype.primGlide = function(b) {
return;
}
// record state: [0]start msecs, [1]duration, [2]startX, [3]startY, [4]endX, [5]endY
interp.activeThread.tmpObj =
[interp.currentMSecs, 1000 * secs, s.scratchX, s.scratchY, destX, destY];
interp.activeThread.tmpObj = [interp.currentMSecs, 1000 * secs, s.scratchX, s.scratchY, destX, destY];
interp.startTimer(secs);
} else {
var state = interp.activeThread.tmpObj;
if (!interp.checkTimer()) {
// in progress: move to intermediate position along path
var frac = (interp.currentMSecs - state[0]) / state[1];
var newX = state[2] + (frac * (state[4] - state[2]));
var newY = state[3] + (frac * (state[5] - state[3]));
var newX = state[2] + frac * (state[4] - state[2]);
var newY = state[3] + frac * (state[5] - state[3]);
moveSpriteTo(s, newX, newY);
} else {
// finished: move to final position and clear state
moveSpriteTo(s, state[4], state[5]);
interp.activeThread.tmpObj = null;
}
}
}
}
};
MotionAndPenPrims.prototype.primChangeX = function(b) {
var s = interp.targetSprite();
if (s != null) moveSpriteTo(s, s.scratchX + interp.arg(b, 0), s.scratchY);
}
};
MotionAndPenPrims.prototype.primSetX = function(b) {
var s = interp.targetSprite();
if (s != null) moveSpriteTo(s, interp.arg(b, 0), s.scratchY);
}
};
MotionAndPenPrims.prototype.primChangeY = function(b) {
var s = interp.targetSprite();
if (s != null) moveSpriteTo(s, s.scratchX, s.scratchY + interp.arg(b, 0));
}
};
MotionAndPenPrims.prototype.primSetY = function(b) {
var s = interp.targetSprite();
if (s != null) moveSpriteTo(s, s.scratchX, interp.arg(b, 0));
}
};
MotionAndPenPrims.prototype.primBounceOffEdge = function(b) {
var s = interp.targetSprite();
@ -164,7 +162,7 @@ MotionAndPenPrims.prototype.primBounceOffEdge = function(b) {
if (!turnAwayFromEdge(s)) return;
ensureOnStageOnBounce(s);
if (s.visible) interp.redraw();
}
};
MotionAndPenPrims.prototype.primSetRotationStyle = function(b) {
var s = interp.targetSprite();
@ -175,95 +173,94 @@ MotionAndPenPrims.prototype.primSetRotationStyle = function(b) {
else if (request == 'left-right') rotationStyle = 'leftRight';
else if (request == 'none') rotationStyle = 'none';
s.setRotationStyle(rotationStyle);
}
};
MotionAndPenPrims.prototype.primXPosition = function(b) {
var s = interp.targetSprite();
return (s != null) ? s.scratchX : 0;
}
return s != null ? s.scratchX : 0;
};
MotionAndPenPrims.prototype.primYPosition = function(b) {
var s = interp.targetSprite();
return (s != null) ? s.scratchY : 0;
}
return s != null ? s.scratchY : 0;
};
MotionAndPenPrims.prototype.primDirection = function(b) {
var s = interp.targetSprite();
return (s != null) ? s.direction : 0;
}
return s != null ? s.direction : 0;
};
MotionAndPenPrims.prototype.primClear = function(b) {
runtime.stage.clearPenStrokes();
interp.redraw();
}
};
MotionAndPenPrims.prototype.primPenDown = function(b) {
var s = interp.targetSprite();
if (s != null) s.penIsDown = true;
stroke(s, s.scratchX, s.scratchY, s.scratchX + 0.2, s.scratchY + 0.2);
interp.redraw();
}
};
MotionAndPenPrims.prototype.primPenUp = function(b) {
var s = interp.targetSprite();
if (s != null) s.penIsDown = false;
}
};
MotionAndPenPrims.prototype.primSetPenColor = function(b) {
var s = interp.targetSprite();
if (s != null) s.setPenColor(interp.arg(b, 0));
}
};
MotionAndPenPrims.prototype.primSetPenHue = function(b) {
var s = interp.targetSprite();
if (s != null) s.setPenHue(interp.arg(b, 0));
}
};
MotionAndPenPrims.prototype.primChangePenHue = function(b) {
var s = interp.targetSprite();
if (s != null) s.setPenHue(s.penHue + interp.arg(b, 0));
}
};
MotionAndPenPrims.prototype.primSetPenShade = function(b) {
var s = interp.targetSprite();
if (s != null) s.setPenShade(interp.arg(b, 0));
}
};
MotionAndPenPrims.prototype.primChangePenShade = function(b) {
var s = interp.targetSprite();
if (s != null) s.setPenShade(s.penShade + interp.arg(b, 0));
}
};
MotionAndPenPrims.prototype.primSetPenSize = function(b) {
var s = interp.targetSprite();
var w = Math.max(0, Math.min(interp.arg(b, 0), 100));
if (s != null) s.penWidth = w;
}
};
MotionAndPenPrims.prototype.primChangePenSize = function(b) {
var s = interp.targetSprite();
var w = Math.max(0, Math.min(s.penWidth + interp.arg(b, 0), 100));
if (s != null) s.penWidth = w;
}
};
MotionAndPenPrims.prototype.primStamp = function(b) {
var s = interp.targetSprite();
s.stamp(runtime.stage.lineCache, 100);
}
};
MotionAndPenPrims.prototype.primStampTransparent = function(b) {
var s = interp.targetSprite();
var transparency = Math.max(0, Math.min(interp.arg(b, 0), 100));
var alpha = 100 - transparency;
s.stamp(runtime.stage.lineCache, alpha);
}
};
// Helpers
var stroke = function(s, oldX, oldY, newX, newY) {
runtime.stage.stroke([oldX, oldY],
[newX, newY], s.penWidth, s.penColorCache);
runtime.stage.stroke([oldX, oldY], [newX, newY], s.penWidth, s.penColorCache);
interp.redraw();
}
};
var mouseOrSpritePosition = function(arg) {
if (arg == "_mouse_") {
@ -275,7 +272,7 @@ var mouseOrSpritePosition = function(arg) {
return new Point(s.scratchX, s.scratchY);
}
return null;
}
};
var moveSpriteTo = function(s, newX, newY) {
var oldX = s.scratchX;
@ -283,8 +280,8 @@ var moveSpriteTo = function(s, newX, newY) {
s.setXY(newX, newY);
s.keepOnStage();
if (s.penIsDown) stroke(s, oldX, oldY, s.scratchX, s.scratchY);
if ((s.penIsDown) || (s.visible)) interp.redraw();
}
if (s.penIsDown || s.visible) interp.redraw();
};
var turnAwayFromEdge = function(s) {
// turn away from the nearest edge if it's close enough; otherwise do nothing
@ -298,23 +295,23 @@ var turnAwayFromEdge = function(s) {
var d4 = Math.max(0, 360 - r.bottom);
// find the nearest edge
var e = 0, minDist = 100000;
if (d1 < minDist) { minDist = d1; e = 1 }
if (d2 < minDist) { minDist = d2; e = 2 }
if (d3 < minDist) { minDist = d3; e = 3 }
if (d4 < minDist) { minDist = d4; e = 4 }
if (d1 < minDist) { minDist = d1; e = 1; }
if (d2 < minDist) { minDist = d2; e = 2; }
if (d3 < minDist) { minDist = d3; e = 3; }
if (d4 < minDist) { minDist = d4; e = 4; }
if (minDist > 0) return false; // not touching to any edge
// point away from nearest edge
var radians = ((90 - s.direction) * Math.PI) / 180;
var radians = (90 - s.direction) * Math.PI / 180;
var dx = Math.cos(radians);
var dy = -Math.sin(radians);
if (e == 1) { dx = Math.max(0.2, Math.abs(dx)) }
if (e == 2) { dy = Math.max(0.2, Math.abs(dy)) }
if (e == 3) { dx = 0 - Math.max(0.2, Math.abs(dx)) }
if (e == 4) { dy = 0 - Math.max(0.2, Math.abs(dy)) }
var newDir = ((180 * Math.atan2(dy, dx)) / Math.PI) + 90;
if (e == 1) { dx = Math.max(0.2, Math.abs(dx)); }
if (e == 2) { dy = Math.max(0.2, Math.abs(dy)); }
if (e == 3) { dx = 0 - Math.max(0.2, Math.abs(dx)); }
if (e == 4) { dy = 0 - Math.max(0.2, Math.abs(dy)); }
var newDir = Math.atan2(dy, dx) * 180 / Math.PI + 90;
s.direction = newDir;
return true;
}
};
var ensureOnStageOnBounce = function(s) {
var r = s.getRect();
@ -326,4 +323,4 @@ var ensureOnStageOnBounce = function(s) {
if (r.bottom > 360) {
moveSpriteTo(s, s.scratchX, s.scratchY + (r.bottom - 360));
}
}
};

View file

@ -26,57 +26,57 @@ var Primitives = function() {}
Primitives.prototype.addPrimsTo = function(primTable) {
// Math primitives
primTable["+"] = function(b) { return interp.arg(b, 0) + interp.arg(b, 1) };
primTable["-"] = function(b) { return interp.arg(b, 0) - interp.arg(b, 1) };
primTable["*"] = function(b) { return interp.arg(b, 0) * interp.arg(b, 1) };
primTable["/"] = function(b) { return interp.arg(b, 0) / interp.arg(b, 1) };
primTable["%"] = function(b) { return interp.arg(b, 0) % interp.arg(b, 1) };
primTable["+"] = function(b) { return interp.arg(b, 0) + interp.arg(b, 1); };
primTable["-"] = function(b) { return interp.arg(b, 0) - interp.arg(b, 1); };
primTable["*"] = function(b) { return interp.arg(b, 0) * interp.arg(b, 1); };
primTable["/"] = function(b) { return interp.arg(b, 0) / interp.arg(b, 1); };
primTable["%"] = function(b) { return interp.arg(b, 0) % interp.arg(b, 1); };
primTable["randomFrom:to:"] = this.primRandom;
primTable["<"] = function(b) { return (interp.arg(b, 0) < interp.arg(b, 1)) };
primTable["="] = function(b) { return (interp.arg(b, 0) == interp.arg(b, 1)) };
primTable[">"] = function(b) { return (interp.arg(b, 0) > interp.arg(b, 1)) };
primTable["&"] = function(b) { return interp.arg(b, 0) && interp.arg(b, 1) };
primTable["|"] = function(b) { return interp.arg(b, 0) || interp.arg(b, 1) };
primTable["<"] = function(b) { return (interp.arg(b, 0) < interp.arg(b, 1)); };
primTable["="] = function(b) { return (interp.arg(b, 0) == interp.arg(b, 1)); };
primTable[">"] = function(b) { return (interp.arg(b, 0) > interp.arg(b, 1)); };
primTable["&"] = function(b) { return interp.arg(b, 0) && interp.arg(b, 1); };
primTable["|"] = function(b) { return interp.arg(b, 0) || interp.arg(b, 1); };
primTable["not"] = function(b) { return !interp.arg(b, 0) };
primTable["abs"] = function(b) { return Math.abs(interp.arg(b, 0)) };
primTable["sqrt"] = function(b) { return Math.sqrt(interp.arg(b, 0)) };
primTable["\\\\"] = this.primModulo;
primTable["rounded"] = function(b) { return Math.round(interp.arg(b, 0)) };
primTable["rounded"] = function(b) { return Math.round(interp.arg(b, 0)); };
primTable["computeFunction:of:"] = this.primMathFunction;
// String primitives
primTable["concatenate:with:"] = function(b) { return "" + interp.arg(b, 0) + interp.arg(b, 1) };
primTable["concatenate:with:"] = function(b) { return "" + interp.arg(b, 0) + interp.arg(b, 1); };
primTable["letter:of:"] = this.primLetterOf;
primTable["stringLength:"] = function(b) { return interp.arg(b, 0).length };
primTable["stringLength:"] = function(b) { return interp.arg(b, 0).length; };
new VarListPrims().addPrimsTo(primTable);
new MotionAndPenPrims().addPrimsTo(primTable);
new LooksPrims().addPrimsTo(primTable);
new SensingPrims().addPrimsTo(primTable);
new SoundPrims().addPrimsTo(primTable);
}
Primitives.prototype.primRandom = function(b) {
var n1 = interp.arg(b, 0);
var n2 = interp.arg(b, 1);
var low = (n1 <= n2) ? n1 : n2;
var hi = (n1 <= n2) ? n2 : n1;
if(low == hi) return low;
var low = n1 <= n2 ? n1 : n2;
var hi = n1 <= n2 ? n2 : n1;
if (low == hi) return low;
// if both low and hi are ints, truncate the result to an int
if ((Math.floor(low) == low) && (Math.floor(hi) == hi)) {
return low + Math.floor(Math.random() * ((hi + 1) - low));
if (Math.floor(low) == low && Math.floor(hi) == hi) {
return low + Math.floor(Math.random() * (hi + 1 - low));
}
return (Math.random() * (hi - low)) + low;
return Math.random() * (hi - low) + low;
}
Primitives.prototype.primLetterOf = function(b) {
var s = interp.arg(b, 1);
var i = interp.arg(b, 0) - 1;
if ((i < 0) || (i >= s.length)) return "";
if (i < 0 || i >= s.length) return "";
return s.charAt(i);
}
Primitives.prototype.primModulo = function(b) {
var modulus = interp.arg(b, 1);
var n = interp.arg(b, 0) % modulus;
@ -90,14 +90,14 @@ Primitives.prototype.primMathFunction = function(b) {
switch(op) {
case "abs": return Math.abs(n);
case "sqrt": return Math.sqrt(n);
case "sin": return Math.sin((Math.PI * n) / 180);
case "cos": return Math.cos((Math.PI * n) / 180);
case "tan": return Math.tan((Math.PI * n) / 180);
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 "sin": return Math.sin(n * Math.PI / 180);
case "cos": return Math.cos(n * Math.PI / 180);
case "tan": return Math.tan(n * Math.PI / 180);
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 "log": return Math.log(n) / Math.LN10;
case "e ^": return Math.exp(n);
case "10 ^": return Math.exp(n * Math.LN10);
}

View file

@ -15,13 +15,13 @@
'use strict';
var SensingPrims = function() {}
var SensingPrims = function() {};
SensingPrims.prototype.addPrimsTo = function(primTable) {
primTable['touching:'] = this.primTouching;
primTable['touchingColor:'] = this.primTouchingColor;
primTable['color:sees:'] = this.primColorTouchingColor;
primTable['keyPressed:'] = this.primKeyPressed;
primTable['mousePressed'] = function(b) { return runtime.mouseDown; };
primTable['mouseX'] = function(b) { return runtime.mousePos[0]; };
@ -30,29 +30,29 @@ SensingPrims.prototype.addPrimsTo = function(primTable) {
primTable['getAttribute:of:'] = this.primGetAttribute;
primTable['timeAndDate'] = this.primTimeDate;
primTable['timeAndDate'] = function(b){ return runtime.getTimeString(interp.arg(b, 0)); };
primTable['timestamp'] = this.primTimestamp;
}
};
SensingPrims.prototype.primTouching = function(b) {
var s = interp.targetSprite();
if (s == null || !s.visible) return false;
var arg = interp.arg(b, 0);
if (arg == '_edge_') {
return false; // TODO
}
if (arg == '_mouse_') {
return false; // TODO
}
var s2 = runtime.spriteNamed(arg);
if (s2 == null || !s2.visible) return false;
return spriteHitTest(s, s2);
}
};
SensingPrims.prototype.primTouchingColor = function(b) {
var s = interp.targetSprite();
if (s == null || !s.visible) return false;
@ -60,8 +60,8 @@ SensingPrims.prototype.primTouchingColor = function(b) {
var color = interp.arg(b, 0);
return stageColorHitTest(s, color);
}
};
SensingPrims.prototype.primColorTouchingColor = function(b) {
var s = interp.targetSprite();
if (s == null || !s.visible) return false;
@ -70,8 +70,8 @@ SensingPrims.prototype.primColorTouchingColor = function(b) {
var stageColor = interp.arg(b, 1);
return stageColorByColorHitTest(s, myColor, stageColor);
}
};
var spriteHitTest = function(a, b) {
var hitCanvas = document.createElement('canvas');
hitCanvas.width = 480;
@ -91,8 +91,8 @@ var spriteHitTest = function(a, b) {
}
}
return false;
}
};
var stageColorHitTest = function(target, color) {
var r, g, b;
r = (color >> 16);
@ -123,18 +123,18 @@ var stageColorHitTest = function(target, color) {
return true;
}
return false;
}
};
var stageColorByColorHitTest = function(target, myColor, otherColor) {
var threshold_acceptable = function(a, b, c, x, y, z) {
diff_a = Math.abs(a-x);
diff_b = Math.abs(b-y);
diff_c = Math.abs(c-z);
var diff_a = Math.abs(a-x);
var diff_b = Math.abs(b-y);
var diff_c = Math.abs(c-z);
if (diff_a + diff_b + diff_c < 100) {
return true;
}
return false;
}
};
var targetCanvas = document.createElement('canvas');
targetCanvas.width = 480;
targetCanvas.height = 360;
@ -157,23 +157,23 @@ var stageColorByColorHitTest = function(target, myColor, otherColor) {
var hitCanvas = document.createElement('canvas');
hitCanvas.width = 480;
hitCanvas.height = 360;
hitCtx = hitCanvas.getContext('2d');
var hitCtx = hitCanvas.getContext('2d');
$.each(runtime.sprites, function(i, sprite) {
if (sprite != target)
if (sprite != target) {
sprite.stamp(hitCtx, 100);
}
});
var hitData = hitCtx.getImageData(0, 0, hitCanvas.width, hitCanvas.height).data;
var pxCount = targetData.length;
for (var i = 0; i < pxCount; i += 4) {
if (threshold_acceptable(targetData[i], targetData[i+1], targetData[i+2], mr, mg, mb)
&& threshold_acceptable(hitData[i], hitData[i+1], hitData[i+2], or, og, ob)) {
if (threshold_acceptable(targetData[i], targetData[i+1], targetData[i+2], mr, mg, mb) && threshold_acceptable(hitData[i], hitData[i+1], hitData[i+2], or, og, ob)) {
return true;
}
}
}
return false;
}
};
SensingPrims.prototype.primKeyPressed = function(b) {
var key = interp.arg(b, 0);
var ch = key.charCodeAt(0);
@ -184,8 +184,8 @@ SensingPrims.prototype.primKeyPressed = function(b) {
if (key == "down arrow") ch = 40;
if (key == "space") ch = 32;
return (typeof(runtime.keysDown[ch]) != 'undefined');
}
};
SensingPrims.prototype.primDistanceTo = function(b) {
var s = interp.targetSprite();
var p = mouseOrSpritePosition(interp.arg(b, 0));
@ -193,8 +193,8 @@ SensingPrims.prototype.primDistanceTo = function(b) {
var dx = p.x - s.scratchX;
var dy = p.y - s.scratchY;
return Math.sqrt((dx * dx) + (dy * dy));
}
};
SensingPrims.prototype.primGetAttribute = function(b) {
var attr = interp.arg(b, 0);
var targetSprite = runtime.spriteNamed(interp.arg(b, 1));
@ -207,7 +207,7 @@ SensingPrims.prototype.primGetAttribute = function(b) {
if (attr == 'size') return targetSprite.getSize();
if (attr == 'volume') return targetSprite.volume;
return 0;
}
};
SensingPrims.prototype.primTimeDate = function(b) {
var dt = interp.arg(b, 0);
@ -220,13 +220,16 @@ SensingPrims.prototype.primTimeDate = function(b) {
if (dt == 'minute') return now.getMinutes();
if (dt == 'second') return now.getSeconds();
return 0;
}
};
SensingPrims.prototype.primTimestamp = function(b) {
var now = new Date(), epoch = new Date(2000,0,1), dst = now.getTimezoneOffset() - epoch.getTimezoneOffset(), msSince = now.getTime() - epoch.getTime();
msSince += (now.getTimezoneOffset() - dst) * 60000;
return msSince / 86400000;
}
var now = new Date();
var epoch = new Date(2000, 0, 1);
var dst = now.getTimezoneOffset() - epoch.getTimezoneOffset();
var msSince = now.getTime() - epoch.getTime();
msSince += (now.getTimezoneOffset() - dst) * 60000;
return msSince / 86400000;
};
// Helpers
SensingPrims.prototype.mouseOrSpritePosition = function(arg) {
@ -239,4 +242,4 @@ SensingPrims.prototype.mouseOrSpritePosition = function(arg) {
return new Point(s.scratchX, s.scratchY);
}
return null;
}
};

View file

@ -15,7 +15,7 @@
'use strict';
var SoundPrims = function() {}
var SoundPrims = function() {};
SoundPrims.prototype.addPrimsTo = function(primTable) {
primTable['playSound:'] = this.primPlaySound;
@ -34,7 +34,7 @@ SoundPrims.prototype.addPrimsTo = function(primTable) {
primTable['changeTempoBy:'] = function(b) { runtime.stage.data.tempoBPM = runtime.stage.data.tempoBPM + interp.arg(b, 0); };
primTable['setTempoTo:'] = function(b) { runtime.stage.data.tempoBPM = interp.arg(b, 0); };
primTable['tempo'] = function(b) { return runtime.stage.data.tempoBPM; };
}
};
var playSound = function(snd) {
if (snd.source) {
@ -42,11 +42,11 @@ var playSound = function(snd) {
snd.source.noteOff(0);
snd.source = null;
}
snd.source = runtime.audioContext.createBufferSource();
snd.source.buffer = snd.buffer;
snd.source.connect(runtime.audioGain);
// Track the sound's completion state
snd.source.done = false;
snd.source.finished = function() {
@ -64,7 +64,7 @@ var playSound = function(snd) {
runtime.audioPlaying.push(snd);
snd.source.noteOn(0);
return snd.source;
}
};
var playDrum = function(drum, secs, client) {
var player = SoundBank.getDrumPlayer(drum, secs);
@ -81,9 +81,9 @@ var playDrum = function(drum, secs, client) {
runtime.notesPlaying.splice(i, 1);
}
}
window.setTimeout(source.finished, secs * 1000);
window.setTimeout(source.finished, secs * 1000);
return player;
}
};
var playNote = function(instrument, midiKey, secs, client) {
var player = SoundBank.getNotePlayer(instrument, midiKey);
@ -99,9 +99,9 @@ var playNote = function(instrument, midiKey, secs, client) {
runtime.notesPlaying.splice(i, 1);
}
}
window.setTimeout(source.finished, secs * 1000);
window.setTimeout(source.finished, secs * 1000);
return player;
}
};
var stopAllSounds = function() {
var oldPlaying = runtime.audioPlaying;
@ -121,14 +121,14 @@ var stopAllSounds = function() {
oldPlaying[s].finished();
}
}
}
};
SoundPrims.prototype.primPlaySound = function(b) {
var s = interp.targetSprite();
if (s == null) return;
var snd = s.soundNamed(interp.arg(b, 0));
if (snd != null) playSound(snd);
}
};
SoundPrims.prototype.primPlaySoundUntilDone = function(b) {
var activeThread = interp.activeThread;
@ -145,11 +145,11 @@ SoundPrims.prototype.primPlaySoundUntilDone = function(b) {
} else {
interp.yield = true;
}
}
};
var beatsToSeconds = function(beats) {
return (beats * 60) / runtime.stage.data.tempoBPM;
}
return beats * 60 / runtime.stage.data.tempoBPM;
};
SoundPrims.prototype.primPlayNote = function(b) {
var s = interp.targetSprite();
@ -162,7 +162,7 @@ SoundPrims.prototype.primPlayNote = function(b) {
} else {
interp.checkTimer();
}
}
};
SoundPrims.prototype.primPlayDrum = function(b) {
var s = interp.targetSprite();
@ -175,7 +175,7 @@ SoundPrims.prototype.primPlayDrum = function(b) {
} else {
interp.checkTimer();
}
}
};
SoundPrims.prototype.primPlayRest = function(b) {
var s = interp.targetSprite();
@ -186,28 +186,28 @@ SoundPrims.prototype.primPlayRest = function(b) {
} else {
interp.checkTimer();
}
}
};
SoundPrims.prototype.primSetInstrument = function(b) {
var s = interp.targetSprite();
if (s != null) s.instrument = interp.arg(b, 0);
}
};
SoundPrims.prototype.primStopAllSounds = function(b) {
stopAllSounds();
}
};
SoundPrims.prototype.primChangeVolume = function(b) {
var s = interp.targetSprite();
if (s != null) s.volume += interp.arg(b, 0);
}
};
SoundPrims.prototype.primSetVolume = function(b) {
var s = interp.targetSprite();
if (s != null) s.volume = interp.arg(b, 0);
}
};
SoundPrims.prototype.primVolume = function(b) {
var s = interp.targetSprite();
return (s != null) ? s.volume : 0;
}
return s != null ? s.volume : 0;
};

View file

@ -34,7 +34,7 @@ VarListPrims.prototype.addPrimsTo = function(primTable) {
primTable['lineCountOfList:'] = this.primListLength;
primTable['getLine:ofList:'] = this.primListGetLine;
primTable['list:contains:'] = this.primListContains;
}
};
// Variable primitive implementations
@ -42,21 +42,23 @@ VarListPrims.prototype.primReadVar = function(b) {
var s = interp.targetSprite();
if (s == null) return;
var targetVar = interp.arg(b, 0);
if (targetVar in s.variables)
if (targetVar in s.variables) {
return s.variables[targetVar];
else if (targetVar in runtime.stage.variables)
} else if (targetVar in runtime.stage.variables) {
return runtime.stage.variables[targetVar];
}
}
};
VarListPrims.prototype.primSetVar = function(b) {
var s = interp.targetSprite();
if (s == null) return;
var targetVar = interp.arg(b, 0);
if (targetVar in s.variables)
if (targetVar in s.variables) {
s.variables[targetVar] = interp.arg(b, 1);
else if (targetVar in runtime.stage.variables)
} else if (targetVar in runtime.stage.variables) {
runtime.stage.variables[targetVar] = interp.arg(b, 1);
}
}
};
VarListPrims.prototype.primChangeVar = function(b) {
var s = interp.targetSprite();
@ -69,7 +71,7 @@ VarListPrims.prototype.primChangeVar = function(b) {
runtime.stage.variables[targetVar] = parseFloat(runtime.stage.variables[targetVar]);
runtime.stage.variables[targetVar] += interp.arg(b, 1);
}
}
};
VarListPrims.prototype.primHideVar = function(b) {
var targetVar = interp.arg(b, 0);
@ -79,7 +81,7 @@ VarListPrims.prototype.primHideVar = function(b) {
return;
}
}
}
};
VarListPrims.prototype.primShowVar = function(b) {
var targetVar = interp.arg(b, 0);
@ -89,11 +91,11 @@ VarListPrims.prototype.primShowVar = function(b) {
return;
}
}
}
};
// List primitive implementations
// Take a list name and target sprite and return the JS list itself
// Take a list name and target sprite and return the JS list itself
var findList = function(targetSprite, listName) {
if (targetSprite == null) targetSprite = runtime.stage;
if (listName in targetSprite.lists) {
@ -102,20 +104,20 @@ var findList = function(targetSprite, listName) {
return runtime.stage.lists[listName].contents;
}
return null;
}
};
VarListPrims.prototype.primReadList = function(b) {
var list = findList(interp.targetSprite(), interp.arg(b, 0));
if (list) {
var allOne = list.map(function(val){return val.length}).reduce(function(old,val){return old+val},0)===list.length;
return list.join(allOne?'':' ');
var allOne = list.map(function(val) { return val.length; }).reduce(function(old,val) { return old + val; }, 0) === list.length;
return list.join(allOne ? '' : ' ');
}
}
};
VarListPrims.prototype.primListAppend = function(b) {
var list = findList(interp.targetSprite(), interp.arg(b, 1));
if (list) list.push(interp.arg(b, 0));
}
};
VarListPrims.prototype.primListDeleteLine = function(b) {
var list = findList(interp.targetSprite(), interp.arg(b, 1));
@ -124,41 +126,42 @@ VarListPrims.prototype.primListDeleteLine = function(b) {
if (line == 'all' || list.length == 0) list.length = 0;
else if (line == 'last') list.splice(list.length - 1, 1);
else if (parseInt(line) - 1 in list) list.splice(parseInt(line) - 1, 1);
}
};
VarListPrims.prototype.primListInsertAt = function(b) {
var list = findList(interp.targetSprite(), interp.arg(b, 2));
if (!list) return;
var newItem = interp.arg(b, 0);
var position = interp.arg(b, 1);
if (position == 'last') position = list.length;
else if (position == 'random') position = Math.round(Math.random() * list.length);
else position = parseInt(position) - 1;
if (position > list.length) return;
list.splice(position, 0, newItem);
}
};
VarListPrims.prototype.primListSetLine = function(b) {
var list = findList(interp.targetSprite(), interp.arg(b, 1));
if (!list) return;
var newItem = interp.arg(b, 2);
var position = interp.arg(b, 0);
if (position == 'last') position = list.length - 1;
else if (position == 'random') position = Math.floor(Math.random() * list.length);
else position = parseInt(position) - 1;
if (position > list.length - 1) return;
list[position] = newItem;
}
};
VarListPrims.prototype.primListLength = function(b) {
var list = findList(interp.targetSprite(), interp.arg(b, 0));
if (!list) return 0;
return list.length;
}
};
VarListPrims.prototype.primListGetLine = function(b) {
var list = findList(interp.targetSprite(), interp.arg(b, 1));
@ -169,7 +172,7 @@ VarListPrims.prototype.primListGetLine = function(b) {
else if (line == 'last') line = list.length;
else if (list.length < line) return 0;
return list[line - 1];
}
};
VarListPrims.prototype.primListContains = function(b) {
var list = findList(interp.targetSprite(), interp.arg(b, 0));
@ -177,4 +180,4 @@ VarListPrims.prototype.primListContains = function(b) {
var searchItem = interp.arg(b, 1);
if (parseFloat(searchItem) == searchItem) searchItem = parseFloat(searchItem);
return $.inArray(searchItem, list) > -1;
}
};

View file

@ -65,11 +65,11 @@ var NotePlayer = function(wavFileData, originalPitch, loopStart, loopEnd, env) {
if (env) {
this.attackEnd = env[0] * 44.100;
if (this.attackEnd > 0) this.attackRate = Math.pow(33000, 1 / this.attackEnd);
this.holdEnd = this.attackEnd + (env[1] * 44.100);
this.holdEnd = this.attackEnd + env[1] * 44.100;
var decayCount = env[2] * 44100;
this.decayRate = (decayCount == 0) ? 1 : Math.pow(33000, -1 / decayCount);
this.decayRate = decayCount == 0 ? 1 : Math.pow(33000, -1 / decayCount);
}
}
};
NotePlayer.prototype = Object.create(SoundDecoder.prototype);
NotePlayer.prototype.constructor = NotePlayer;
@ -79,14 +79,14 @@ NotePlayer.prototype.setNoteAndDuration = function(midiKey, secs) {
var pitch = 440 * Math.pow(2, (midiKey - 69) / 12); // midi key 69 is A (440 Hz)
this.stepSize = pitch / (2 * this.originalPitch); // adjust for original sampling rate of 22050
this.setDuration(secs);
}
};
NotePlayer.prototype.setDuration = function(secs) {
this.samplesSinceStart = 0;
this.samplesRemaining = 44100 * secs;
if (!this.isLooped) this.samplesRemaining = Math.min(this.samplesRemaining, this.endOffset / this.stepSize);
this.envelopeValue = (this.attackEnd > 0) ? 1 / 33000 : 1;
}
this.envelopeValue = this.attackEnd > 0 ? 1 / 33000 : 1;
};
NotePlayer.prototype.interpolatedSample = function() {
if (this.samplesRemaining-- <= 0) { this.noteFinished(); return 0; }
@ -99,11 +99,11 @@ NotePlayer.prototype.interpolatedSample = function() {
var frac = this.index - i;
var curr = this.rawSample(i);
var next = this.rawSample(i + 1);
var sample = (curr + (frac * (next - curr))) / 100000; // xxx 32000; attenuate...
var sample = (curr + frac * (next - curr)) / 100000; // xxx 32000; attenuate...
if (this.samplesRemaining < 1000) sample *= (this.samplesRemaining / 1000.0); // relaase phease
this.updateEnvelope();
return this.envelopeValue * sample;
}
};
NotePlayer.prototype.rawSample = function(sampleIndex) {
if (sampleIndex >= this.endOffset) {
@ -112,8 +112,8 @@ NotePlayer.prototype.rawSample = function(sampleIndex) {
}
var byteIndex = 2 * sampleIndex;
var result = (this.soundData[byteIndex + 1] << 8) + this.soundData[byteIndex];
return (result <= 32767) ? result : result - 65536;
}
return result <= 32767 ? result : result - 65536;
};
NotePlayer.prototype.updateEnvelope = function() {
// Compute envelopeValue for the current sample.
@ -125,4 +125,4 @@ NotePlayer.prototype.updateEnvelope = function() {
} else if (this.samplesSinceStart > this.holdEnd) {
if (this.decayRate < 1) this.envelopeValue *= this.decayRate;
}
}
};

View file

@ -178,24 +178,24 @@ SoundBank.getNotePlayer = function(instNum, midiKey) {
// Return a NotePlayer for the given Scratch 2.0 instrument number (1..21)
// and MIDI key (0..127). If the instrument is out of range, use 1.
var r = SoundBank.getNoteRecord(instNum - 1, midiKey);
var env = (r.length > 5) ? r[5] : null;
var env = r.length > 5 ? r[5] : null;
return new NotePlayer(Instr.samples[r[1]], SoundBank.pitchForKey(r[2]), r[3], r[4], env);
}
};
SoundBank.getNoteRecord = function(instNum, midiKey) {
// Get a note record for the given instrument number.
if ((instNum < 0) || (instNum >= SoundBank.instruments.length)) instNum = 0;
if (instNum < 0 || instNum >= SoundBank.instruments.length) instNum = 0;
var keyRanges = SoundBank.instruments[instNum];
for (var r = 0; r < keyRanges.length; r++) {
var topOfKeyRange = keyRanges[r][0];
if (midiKey <= topOfKeyRange) return keyRanges[r];
}
return keyRanges[keyRanges.length - 1]; // return the note record for the top key range.
}
};
SoundBank.pitchForKey = function(midiKey) {
return 440 * Math.pow(2, (midiKey - 69) / 12); // midi key 69 is A=440 Hz
}
};
SoundBank.getDrumPlayer = function(drumNum, secs) {
// Return a NotePlayer for the given drum number.
@ -210,6 +210,4 @@ SoundBank.getDrumPlayer = function(drumNum, secs) {
var player = new NotePlayer(Instr.samples[entry[0]], SoundBank.pitchForKey(60), loopStart, loopEnd, env);
player.setNoteAndDuration(60 + entry[1], 0);
return player;
}
};

View file

@ -22,7 +22,7 @@
var SoundDecoder = function(wavFileData) {
this.scratchSound = null;
this.soundData = null;
this.startOffset = 0;
this.endOffset = 0;
@ -37,10 +37,10 @@ var SoundDecoder = function(wavFileData) {
this.thisSample = 0;
// decoder state
this.sample = 0;
this.sample = 0;
this.index = 0;
this.lastByte = -1; // -1 indicates that there is no saved lastByte
this.nextSample = 0;
this.info = null;
@ -61,12 +61,12 @@ var SoundDecoder = function(wavFileData) {
if (info.bitsPerSample == 16) this.getSample = this.getSample16Uncompressed;
}
}
}
};
SoundDecoder.prototype.noteFinished = function() {
// Called by subclasses to force ending condition to be true in writeSampleData()
this.bytePosition = this.endOffset;
}
};
// Used for Notes and Drums - Web Audio API ScriptProcessorNodes use this
// as a callback function to fill the buffers with sample data.
@ -78,7 +78,7 @@ SoundDecoder.prototype.writeSampleData = function(evt) {
var n = this.interpolatedSample();
output[i] = n;
}
}
};
// For pre-caching the samples of WAV sounds
// Return a full list of samples generated by the decoder.
@ -90,7 +90,7 @@ SoundDecoder.prototype.getAllSamples = function() {
smp = this.interpolatedSample();
}
return samples;
}
};
// Provide the next sample for the buffer
SoundDecoder.prototype.interpolatedSample = function() {
@ -101,11 +101,9 @@ SoundDecoder.prototype.interpolatedSample = function() {
this.fraction -= 1.0;
}
if (this.nextSample == null) { return null; }
var out = (this.fraction == 0) ?
this.thisSample :
this.thisSample + (this.fraction * (this.nextSample - this.thisSample));
return (out) / 32768.0;
}
var out = this.fraction == 0 ? this.thisSample : this.thisSample + this.fraction * (this.nextSample - this.thisSample);
return out / 32768.0;
};
// 16-bit samples, big-endian
SoundDecoder.prototype.getSample16Uncompressed = function() {
@ -119,13 +117,13 @@ SoundDecoder.prototype.getSample16Uncompressed = function() {
result = null;
}
return result;
}
};
// 8-bit samples, uncompressed
SoundDecoder.prototype.getSample8Uncompressed = function() {
if (this.bytePosition >= this.info.sampleDataSize) return null;
return (this.soundData[this.bytePosition++] - 128) << 8;
}
};
/*SoundDecoder.prototype.updateVolume = function() {
if (this.client == null) {
@ -149,13 +147,13 @@ SoundDecoder.stepTable = [
12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
];
SoundDecoder.prototype.getSampleADPCM = function() {
SoundDecoder.prototype.getSampleADPCM = function() {
// Decompress sample data using the IMA ADPCM algorithm.
// Note: Handles only one channel, 4-bits/sample.
// Note: Handles only one channel, 4-bits/sample.
var step = 0, code = 0, delta = 0;
if (((this.bytePosition % this.adpcmBlockSize) == 0) && (this.lastByte < 0)) { // read block header
if (this.bytePosition > (this.info.sampleDataSize - 4)) return null;
if (this.bytePosition % this.adpcmBlockSize == 0 && this.lastByte < 0) { // read block header
if (this.bytePosition > this.info.sampleDataSize - 4) return null;
this.sample = (this.soundData[this.bytePosition + 1] << 8) + this.soundData[this.bytePosition];
if (this.sample > 32767) this.sample -= 65536;
this.index = this.soundData[this.bytePosition + 2];
@ -184,7 +182,7 @@ SoundDecoder.prototype.getSampleADPCM = function() {
if (this.index > 88) this.index = 88;
if (this.index < 0) this.index = 0;
// compute and output sample
this.sample += ((code & 8) ? -delta : delta);
this.sample += code & 8 ? -delta : delta;
if (this.sample > 32767) this.sample = 32767;
if (this.sample < -32768) this.sample = -32768;
return this.sample;

View file

@ -19,11 +19,10 @@
var WAVFile = function() {};
WAVFile.decode = function(waveData) {
// Decode the given WAV file data and return an Object with the format and sample data.
var result = {};
var data = new OffsetBuffer(waveData);
// read WAVE File Header
@ -31,11 +30,11 @@ WAVFile.decode = function(waveData) {
var totalSize = data.readInt();
if (data.getLength() != (totalSize + 8)) console.log("WAVFile: bad RIFF size; ignoring");
if (data.readString(4) != 'WAVE') { console.log("WAVFile: not a WAVE file"); return; }
// read format chunk
var formatChunk = WAVFile.extractChunk('fmt ', data);
if (formatChunk.getLength() < 16) { console.log("WAVFile: format chunk is too small"); return; }
var encoding = formatChunk.readShort();
result.encoding = encoding;
result.channels = formatChunk.readShort();
@ -48,7 +47,7 @@ WAVFile.decode = function(waveData) {
var sampleDataStartAndSize = WAVFile.dataChunkStartAndSize(data);
result.sampleDataStart = sampleDataStartAndSize[0];
result.sampleDataSize = sampleDataStartAndSize[1];
// handle various encodings
if (encoding == 1) {
if (!((result.bitsPerSample == 8) || (result.bitsPerSample == 16))) {
@ -75,8 +74,7 @@ WAVFile.decode = function(waveData) {
return;
}
return result;
}
};
WAVFile.extractChunk = function(desiredType, data) {
// Return the contents of the first chunk of the given type or an empty OffsetBuffer if it is not found.
@ -93,8 +91,7 @@ WAVFile.extractChunk = function(desiredType, data) {
}
}
return new OffsetBuffer(new ArrayBuffer());
}
};
WAVFile.dataChunkStartAndSize = function(data) {
// Return an array with the starting offset and size of the first chunk of the given type.
@ -110,4 +107,4 @@ WAVFile.dataChunkStartAndSize = function(data) {
}
}
return [0, 0]; // chunk not found; bad wave file
}
};

View file

@ -20,70 +20,70 @@
Color = function() {};
Color.fromHSV = function(h, s, v) {
var r, g, b;
h = h % 360;
if (h < 0) h += 360;
s = Math.max(0, Math.min(s, 1));
v = Math.max(0, Math.min(v, 1));
var r, g, b;
h = h % 360;
if (h < 0) h += 360;
s = Math.max(0, Math.min(s, 1));
v = Math.max(0, Math.min(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)));
if (i == 0) { r = v; g = t; b = p; }
else if (i == 1) { r = q; g = v; b = p; }
else if (i == 2) { r = p; g = v; b = t; }
else if (i == 3) { r = p; g = q; b = v; }
else if (i == 4) { r = t; g = p; b = v; }
else if (i == 5) { r = v; g = p; b = q; }
r = Math.floor(r * 255);
g = Math.floor(g * 255);
b = Math.floor(b * 255);
return (r << 16) | (g << 8) | b;
}
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));
if (i == 0) { r = v; g = t; b = p; }
else if (i == 1) { r = q; g = v; b = p; }
else if (i == 2) { r = p; g = v; b = t; }
else if (i == 3) { r = p; g = q; b = v; }
else if (i == 4) { r = t; g = p; b = v; }
else if (i == 5) { r = v; g = p; b = q; }
r = Math.floor(r * 255);
g = Math.floor(g * 255);
b = Math.floor(b * 255);
return (r << 16) | (g << 8) | b;
};
Color.rgb2hsv = function(rgb) {
var h, s, v, x, f, i;
var r = ((rgb >> 16) & 255) / 255;
var g = ((rgb >> 8) & 255) / 255;
var b = (rgb & 255) / 255;
x = Math.min(Math.min(r, g), b);
v = Math.max(Math.max(r, g), b);
if (x == v) return [0, 0, v]; // gray; hue arbitrarily reported as zero
f = (r == x) ? g - b : ((g == x) ? b - r : r - g);
i = (r == x) ? 3 : ((g == x) ? 5 : 1);
h = ((i - (f / (v - x))) * 60) % 360;
s = (v - x) / v;
return [h, s, v];
}
var h, s, v, x, f, i;
var r = ((rgb >> 16) & 255) / 255;
var g = ((rgb >> 8) & 255) / 255;
var b = (rgb & 255) / 255;
x = Math.min(Math.min(r, g), b);
v = Math.max(Math.max(r, g), b);
if (x == v) return [0, 0, v]; // gray; hue arbitrarily reported as zero
f = r == x ? g - b : g == x ? b - r : r - g;
i = r == x ? 3 : g == x ? 5 : 1;
h = ((i - f / (v - x)) * 60) % 360;
s = (v - x) / v;
return [h, s, v];
};
Color.scaleBrightness = function(rgb, scale) {
var hsv = Color.rgb2hsv(rgb);
scale = Math.max(0, Math.min(scale, 1));
return Color.fromHSV(hsv[0], hsv[1], scale * hsv[2]);
}
var hsv = Color.rgb2hsv(rgb);
scale = Math.max(0, Math.min(scale, 1));
return Color.fromHSV(hsv[0], hsv[1], scale * hsv[2]);
};
Color.mixRGB = function(rgb1, rgb2, fraction) {
// Mix rgb1 with rgb2. 0 gives all rgb1, 1 gives rbg2, .5 mixes them 50/50.
if (fraction <= 0) return rgb1;
if (fraction >= 1) return rgb2;
var r1 = (rgb1 >> 16) & 255;
var g1 = (rgb1 >> 8) & 255;
var b1 = rgb1 & 255
var r2 = (rgb2 >> 16) & 255;
var g2 = (rgb2 >> 8) & 255;
var b2 = rgb2 & 255
var r = ((fraction * r2) + ((1.0 - fraction) * r1)) & 255;
var g = ((fraction * g2) + ((1.0 - fraction) * g1)) & 255;
var b = ((fraction * b2) + ((1.0 - fraction) * b1)) & 255;
return (r << 16) | (g << 8) | b;
}
// Mix rgb1 with rgb2. 0 gives all rgb1, 1 gives rbg2, .5 mixes them 50/50.
if (fraction <= 0) return rgb1;
if (fraction >= 1) return rgb2;
var r1 = (rgb1 >> 16) & 255;
var g1 = (rgb1 >> 8) & 255;
var b1 = rgb1 & 255
var r2 = (rgb2 >> 16) & 255;
var g2 = (rgb2 >> 8) & 255;
var b2 = rgb2 & 255
var r = ((fraction * r2) + ((1.0 - fraction) * r1)) & 255;
var g = ((fraction * g2) + ((1.0 - fraction) * g1)) & 255;
var b = ((fraction * b2) + ((1.0 - fraction) * b1)) & 255;
return (r << 16) | (g << 8) | b;
};
Color.random = function() {
// return a random color
var h = 360 * Math.random();
var s = 0.7 + (0.3 * Math.random());
var v = 0.6 + (0.4 * Math.random());
return Color.fromHSV(h, s, v);
}
// return a random color
var h = 360 * Math.random();
var s = 0.7 + (0.3 * Math.random());
var v = 0.6 + (0.4 * Math.random());
return Color.fromHSV(h, s, v);
};

View file

@ -19,63 +19,63 @@
var OffsetBuffer = function(data) {
this.offset = 0;
this.ab = data;
}
};
// Read various datatypes from the ArrayBuffer, seeking the offset.
OffsetBuffer.prototype.readString = function(length) {
var str = this.ab2str(this.ab.slice(this.offset, this.offset + length));
this.offset += length;
return str;
}
};
OffsetBuffer.prototype.readInt = function() {
var num = this.ab2int(this.ab.slice(this.offset, this.offset + 4));
this.offset += 4;
return num;
}
};
OffsetBuffer.prototype.readUint = function() {
var num = this.ab2uint(this.ab.slice(this.offset, this.offset + 4));
this.offset += 4;
return num;
}
};
OffsetBuffer.prototype.readShort = function() {
var num = this.ab2short(this.ab.slice(this.offset, this.offset + 2));
this.offset += 2;
return num;
}
};
OffsetBuffer.prototype.readBytes = function(length) {
var bytes = this.ab.slice(this.offset, this.offset + length);
this.offset += length;
return bytes;
}
};
// Length of the internal buffer
OffsetBuffer.prototype.getLength = function() {
return this.ab.byteLength;
}
};
// Number of bytes remaining from the current offset
OffsetBuffer.prototype.bytesAvailable = function() {
return (this.getLength() - this.offset);
}
return this.getLength() - this.offset;
};
// ArrayBuffer -> JS type conversion methods
OffsetBuffer.prototype.ab2str = function(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
};
// These create Javascript Numbers
OffsetBuffer.prototype.ab2int = function(buf) {
return new Int32Array(buf)[0];
}
};
OffsetBuffer.prototype.ab2uint = function(buf) {
return new Uint32Array(buf)[0];
}
};
OffsetBuffer.prototype.ab2short = function(buf) {
return new Int16Array(buf)[0];
}
};

View file

@ -16,7 +16,7 @@
var Point = function(x, y) {
this.x = x;
this.y = y;
}
};
var Rectangle = function(x, y, width, height) {
this.x = x;
@ -27,10 +27,8 @@ var Rectangle = function(x, y, width, height) {
this.right = x + width;
this.top = y;
this.bottom = y + height;
}
};
Rectangle.prototype.intersects = function(other) {
return !(this.left > other.right ||
this.right < other.left ||
this.top > other.bottom ||
this.bottom < other.top);
}
return !(this.left > other.right || this.right < other.left || this.top > other.bottom || this.bottom < other.top);
};

View file

@ -21,40 +21,40 @@ var Timer = function() {
var trials = [];
var last_trial = 0;
var start_time = 0;
}
};
Timer.prototype.time = function() {
return (new Date()).getTime();
}
return Date.now();
};
Timer.prototype.start = function() {
start_time = this.time();
}
};
Timer.prototype.stop = function() {
end = this.time();
last_trial = end - start_time;
trials.push(last_trial);
}
};
Timer.prototype.count = function() {
return trials.length;
}
};
Timer.prototype.average = function() {
sum = 0;
for(i = 0; i < this.count(); i++) {
for (i = 0; i < this.count(); i++) {
sum += trials[i];
}
return sum / this.count();
}
};
Timer.prototype.print = function(element) {
text = "Trial: " + last_trial + "ms" +
text = "Trial: " + last_trial + "ms" +
"<br />\nTrials: " + this.count() + ", Avg: " + this.average() + "ms";
if(element) {
if (element) {
$(element).html(text);
} else {
console.log(text);
}
}
};