Fixed conflicts, ready to merge.

This commit is contained in:
Hardmath123 2013-11-04 20:18:47 -08:00
commit a0c414dbac
9 changed files with 301 additions and 39 deletions

View file

@ -8,6 +8,11 @@ Unimplementable Features on iOS: Image effects for whirl, fisheye, mosaic, and p
More documentation will be added as time permits. Thanks for contributing, and Scratch On!
## Contributions
Thank you for your interest in helping out with the Scratch HTML5 Player. [@sclements](https://github.com/sclements/) is the maintainer of the project and reviews all code before pull requests are approved. Though we appreciate all attempts to contribute, there are some contraints that must be met before pull requests can be approved. Here are our top concerns for contributions: matching the behavior and interface of the Flash player, code cleanliness and organization, and robust well tested logic. CSS goes into player.css (not in the html or javascript). Please use [compare.html](https://github.com/LLK/scratch-html5/blob/master/compare.html) to compare your work with the production Flash player.
## Installation
Running the HTML5 player on your own website, or locally, you will need to have

9
compare.css Normal file
View file

@ -0,0 +1,9 @@
textarea {
width: 450px;
height: 200px;
}
object {
display: block;
}

View file

@ -8,8 +8,9 @@ header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
<title>Scratch HTML5 vs. Flash</title>
<meta charset="utf-8">
<link href="player.css" type="text/css" rel="stylesheet" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
<link href="compare.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>
@ -40,18 +41,16 @@ header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
}
</script>
<script>
var flashLog = null;
$(function() {
// The flashvars tell flash about the project data (and autostart=true)
var flashvars = {
server: encodeURIComponent('scratch.mit.edu'),
debugOps: true,
debugOpCmd: 'debugFlash',
project_id: project_id
};
// Pass in the cloud token for the project
if (window.getCloudToken) {
flashvars.cloud_token = encodeURIComponent(getCloudToken());
}
var params = {
allowscriptaccess: 'always',
allowfullscreen: 'false',
@ -62,7 +61,7 @@ header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
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",
swfobject.embedSWF(swf_url, "flashScratch", "482", "402", "10.2.0",
"http://cdn.scratch.mit.edu/scratchr2/static/expressInstall.swf",
flashvars, params, null, function(e) {
$('#flashScratch').css('visibility', 'visible');
@ -77,11 +76,33 @@ header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
$('#trigger_stop').click(function() {
flashPlayer.ASstopRunning();
});
flashLog = $('#flashDebug');
var jsLog = $('#jsDebug');
interp.debugOps = true;
interp.debugFunc = function(opCount, opName, args) {
var text = opCount + ': '+opName+'('+args.join(', ')+')';
jsLog.val(jsLog.val() + text + "\n");
}
// Setup synchronized scrolling
flashLog.scroll(function() {
jsLog.scrollTop(flashLog.scrollTop());
});
jsLog.scroll(function() {
flashLog.scrollTop(jsLog.scrollTop());
});
});
function debugFlash(opCount, opName, args) {
var text = opCount + ': '+opName+'('+args.join(', ')+')';
flashLog.val(flashLog.val() + text + "\n");
}
</script>
</head>
<body>
<div style="display: inline-block;">
<div class="playerContainer">
<div id="up">&#x25B2;</div>
<div id="ui-top">
<div id="info">Loading&hellip;</div>
@ -98,15 +119,16 @@ header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
<div id="ui-bottom">
<span id="address-hint">http://scratch.mit.edu/projects/</span><input type="text" name="project_id" id="project_id" placeholder="10000160" /><button id='go_project'>&rarr;</button></div>
<div id="down">&#x25BC;</div>
<textarea id="jsDebug"></textarea>
</div>
<div style="display: inline-block;">
<div class="playerContainer">
<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>
<textarea id="flashDebug"></textarea>
</div>
</body>
</html>

View file

@ -8,7 +8,7 @@
<meta name="viewport" content="user-scalable=no, width=540" />
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<link href="player.css" type="text/css" rel="stylesheet" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="js/util/Timer.js"></script>
<script src="js/util/OffsetBuffer.js"></script>
<script src="js/util/Color.js"></script>

View file

@ -91,20 +91,45 @@ IO.prototype.makeObjects = function() {
runtime.stage.attach(runtime.scene);
runtime.stage.attachPenLayer(runtime.scene);
runtime.stage.loadSounds();
// Create the sprites
$.each(this.data.children, function(index, obj) {
// Create the sprites and watchers
function createObj(obj, sprite) {
var newSprite;
if (!obj.cmd) {
newSprite = new Sprite(obj);
} else {
newSprite = new Reporter(obj);
runtime.reporters.push(newSprite);
}
runtime.sprites.push(newSprite);
newSprite.attach(runtime.scene);
if (!obj.cmd)
function createSprite(obj) {
var newSprite = new Sprite(obj);
newSprite.loadSounds();
return newSprite;
}
function createReporter(obj, sprite) {
var newSprite;
if (obj.listName) { // list
if (!(sprite===runtime.stage && !runtime.stage.lists[obj.listName])) { // for local lists, only if in sprite
newSprite = new List(obj, sprite.objName);
runtime.reporters.push(newSprite);
}
} else {
newSprite = new Reporter(obj);
runtime.reporters.push(newSprite);
}
return newSprite;
}
if (obj.objName) { // sprite
newSprite = createSprite(obj);
sprite = newSprite;
} else {
newSprite = createReporter(obj, sprite);
}
if (newSprite) {
runtime.sprites.push(newSprite);
newSprite.attach(runtime.scene);
}
}
$.each(this.data.children, function(index, obj) {
createObj(obj, runtime.stage); // create children of stage - sprites, watchers, and stage's lists
});
$.each(runtime.sprites.filter(function(sprite){return sprite instanceof Sprite}), function(index, sprite) { // list of sprites
$.each(sprite.lists, function(index, list){
createObj(list, sprite); // create local lists
});
});
};

View file

@ -54,6 +54,9 @@ var Interpreter = function() {
this.yield = false;
this.doRedraw = false;
this.opCount = 0; // used to benchmark the interpreter
this.debugOps = false;
this.debugFunc = null;
this.opCount2 = 0;
};
// Utilities for building blocks and sequences of blocks
@ -136,6 +139,14 @@ Interpreter.prototype.stepActiveThread = function() {
// Advance the "program counter" to the next block before running the primitive.
// Control flow primitives (e.g. if) may change activeThread.nextBlock.
this.activeThread.nextBlock = b.nextBlock;
if(this.debugOps && this.debugFunc) {
var finalArgs = new Array(b.args.length);
for(var i=0; i<b.args.length; ++i)
finalArgs[i] = this.arg(b, i);
this.debugFunc(this.opCount2, b.op, finalArgs);
++this.opCount2;
}
b.primFcn(b);
if (this.yield) { this.activeThread.nextBlock = b; return; }
b = this.activeThread.nextBlock; // refresh local variable b in case primitive did some control flow

View file

@ -119,3 +119,81 @@ Reporter.prototype.changeSlider = function() {
var variable = $(this).attr('data-var');
target.variables[variable] = newValue;
};
var List = function(data, sprite) {
this.contents = data.contents;
this.listName = data.listName;
this.height = data.height;
this.width = data.width;
this.x = data.x;
this.y = data.y;
this.z = io.getCount();
this.visible = data.visible;
this.target = sprite;
// this.isPersistent = data.isPersistent;
this.el = null; // jQuery element for list
this.containerEl = null;
this.scrollbar = null;
};
List.prototype.attach = function(scene) {
this.el = $('<div class="list">');
this.el.append('<div class="list-title">'+(this.target==='Stage'?'':this.target+' ')+this.listName);
var c = this.containerEl = $('<div style="overflow:hidden;float:left;position:relative">').appendTo(this.el).width(this.width-13).height(this.height-34);
var s = this.scrollbar = $('<div class="list-scrollbar-container"><div class="list-scrollbar">').appendTo(this.el);
var sb = s.children('.list-scrollbar');
sb.mousedown(function(e){
if (e.which===1) $(this).data({scrolling:true,startY:e.pageY}); // left button
});
$('body').mousemove(function(e){
if (sb.data('scrolling')) {
var offset = parseInt(sb.css('top'))+e.pageY-sb.data('startY');
if (offset < 0) {
offset = 0;
}
if (offset > c.height()-sb.height()) {
offset = c.height()-sb.height();
}
sb.css('top',offset);
c.scrollTop(c.height()/sb.height()*offset);
}
}).mouseup(function(){
if ($.hasData(sb[0],'scrolling')) sb.removeData(['scrolling','startY']);
});
// this.el.append('<div class="list-add">+'); // disabled because it doesn't do anything even in the original
this.el.append('<div class="list-length">length: '+this.contents.length);
scene.append(this.el);
this.update();
this.el.css('left', this.x);
this.el.css('top', this.y);
this.el.width(this.width);
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.contents = findList(runtime.spriteNamed(this.target),this.listName);
this.el.css('display', this.visible ? 'inline-block' : 'none');
if (!this.visible) return;
var c = this.containerEl.html(''); // so that it can be used inside the forEach
this.contents.forEach(function(val,i){
$('<div style="clear:both">').appendTo(c).append('<div class="list-index">'+(i+1),'<div class="list-item">'+val);
});
c.find('.list-index').width(c.find('.list-index').last().width());
c.find('.list-item').width(c.width()-c.find('.list-index').width()-15);
var s = this.scrollbar.height(c.height());
s.children('.list-scrollbar').height(s.height()/c[0].scrollHeight*s.height()).css('display', s.children('.list-scrollbar').height()===c.height() ? 'none' : 'inline-block');
this.el.find('.list-length').text('length: '+this.contents.length);
};
List.prototype.updateLayer = function() {
this.el.css('z-index', this.z);
};

View file

@ -34,6 +34,8 @@ VarListPrims.prototype.addPrimsTo = function(primTable) {
primTable['lineCountOfList:'] = this.primListLength;
primTable['getLine:ofList:'] = this.primListGetLine;
primTable['list:contains:'] = this.primListContains;
primTable['hideList:'] = this.primHideList;
primTable['showList:'] = this.primShowList;
};
// Variable primitive implementations
@ -74,9 +76,9 @@ VarListPrims.prototype.primChangeVar = function(b) {
};
VarListPrims.prototype.primHideVar = function(b) {
var targetVar = interp.arg(b, 0);
var targetVar = interp.arg(b, 0), targetSprite = interp.targetSprite().objName;
for (var r = 0; r < runtime.reporters.length; r++) {
if (runtime.reporters[r].cmd == 'getVar:' && runtime.reporters[r].param == targetVar) {
if (runtime.reporters[r].cmd == 'getVar:' && runtime.reporters[r].param == targetVar && (runtime.reporters[r].target == targetSprite || runtime.reporters[r].target == 'Stage')) {
runtime.reporters[r].visible = false;
return;
}
@ -84,9 +86,9 @@ VarListPrims.prototype.primHideVar = function(b) {
};
VarListPrims.prototype.primShowVar = function(b) {
var targetVar = interp.arg(b, 0);
var targetVar = interp.arg(b, 0), targetSprite = interp.targetSprite().objName;
for (var r = 0; r < runtime.reporters.length; r++) {
if (runtime.reporters[r].cmd == 'getVar:' && runtime.reporters[r].param == targetVar) {
if (runtime.reporters[r].cmd == 'getVar:' && runtime.reporters[r].param == targetVar && (runtime.reporters[r].target == targetSprite || runtime.reporters[r].target == 'Stage')) {
runtime.reporters[r].visible = true;
return;
}
@ -96,7 +98,7 @@ VarListPrims.prototype.primShowVar = function(b) {
// List primitive implementations
// Take a list name and target sprite and return the JS list itself
var findList = function(targetSprite, listName) {
function findList(targetSprite, listName) {
if (targetSprite == null) targetSprite = runtime.stage;
if (listName in targetSprite.lists) {
return targetSprite.lists[listName].contents;
@ -104,7 +106,7 @@ 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));
@ -181,3 +183,23 @@ VarListPrims.prototype.primListContains = function(b) {
if (parseFloat(searchItem) == searchItem) searchItem = parseFloat(searchItem);
return $.inArray(searchItem, list) > -1;
};
VarListPrims.prototype.primHideList = function(b) {
var targetList = interp.arg(b, 0), targetSprite = interp.targetSprite().objName;
for (var r = 0; r < runtime.reporters.length; r++) {
if (runtime.reporters[r] instanceof List && runtime.reporters[r].listName == targetList && (runtime.reporters[r].target == targetSprite || runtime.reporters[r].target == 'Stage')) {
runtime.reporters[r].visible = false;
return;
}
}
};
VarListPrims.prototype.primShowList = function(b) {
var targetList = interp.arg(b, 0), targetSprite = interp.targetSprite().objName;
for (var r = 0; r < runtime.reporters.length; r++) {
if (runtime.reporters[r] instanceof List && runtime.reporters[r].listName == targetList && (runtime.reporters[r].target == targetSprite || runtime.reporters[r].target == 'Stage')) {
runtime.reporters[r].visible = true;
return;
}
}
};

View file

@ -42,14 +42,9 @@ body {
/* Hmm, will this break after the 501st sprite? */
margin-left: 30px;
}
/* The green flag icon. */
#greenSlideFg {
background-color: #000;
opacity: 0.2;
width: 278px;
height: 218px;
padding-top: 142px;
padding-left: 202px;
#greenSlideFg {
background-color: rgba(0,0,0,0.4);
padding: 140px 202px;
}
/* iPad arrow key frame */
@ -108,6 +103,10 @@ body {
clear: both;
text-align: right;
}
.playerContainer {
display: inline-block;
text-align: center;
}
/* Reporter styles */
.reporter-normal {
display: inline-block;
@ -147,6 +146,96 @@ body {
color: #fff;
position: absolute;
}
/* List watcher styles */
.list {
float: left;
border-radius: 7px;
-webkit-border-radius: 7px;
-moz-border-radius: 7px;
border-style: solid;
border-color: gray;
border-width: 2px;
background-color: #C1C4C7;
padding-left: 3px;
font: bold 11px sans-serif;
position: absolute;
}
.list .list-title {
padding-top: 2px;
text-align: center;
color: black;
font-weight: bold;
margin-bottom: 4px;
}
.list .list-scrollbar-container {
float: right;
width: 10px;
position: relative;
}
.list .list-scrollbar {
position: absolute;
top: 0px;
width: 10px;
background-color: #a8aaad;
border-radius: 10px;
}
.list .list-index {
color: rgb(81, 81, 81);
float: left;
padding: 2px;
margin-top: 0px;
text-align: right;
font: bold 11px;
}
.list .list-item {
float: right;
height: 16px;
overflow: hidden;
margin-bottom: 0px;
margin-right: 2px;
padding-top: 2px;
padding-left: 5px;
border-color: white;
border-style: solid;
border-width: 1px;
border-radius: 4px;
background-color: #cc5b22;
color: white;
word-wrap: break-word;
font: bolder 11px sans-serif;
}
.list .list-add {
clear: both;
background-color: #dedede;
width: 12px;
height: 12px;
border-radius: 12px;
color: #707070;
font: bold 10px sans-serif;
text-align: center;
position: absolute;
bottom: 2px;
left: 2px;
}
.list .list-length {
font-size: 10px;
font-weight: normal;
text-align: center;
color: black;
position: absolute;
bottom: 2px;
left: 0px;
right: 0px;
}
/* Say/think bubble styles */
.bubble-container {
position: absolute;
@ -182,6 +271,7 @@ body {
height: 19px;
background: url(img/think-bottom.png) transparent no-repeat;
}
#info {
float: left;
margin-left: 30px;
@ -260,4 +350,4 @@ input[type=text]:focus {
margin-left: 0px;
margin-top: 3px;
margin-bottom: 0px;
}
}