diff --git a/README.md b/README.md index 91a59a9..5777743 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/compare.css b/compare.css new file mode 100644 index 0000000..c28d073 --- /dev/null +++ b/compare.css @@ -0,0 +1,9 @@ +textarea { + width: 450px; + height: 200px; +} + +object { + display: block; +} + diff --git a/compare.html b/compare.html index fd4b25c..54961e8 100644 --- a/compare.html +++ b/compare.html @@ -8,8 +8,9 @@ header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past Scratch HTML5 vs. Flash - - + + + @@ -40,18 +41,16 @@ header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past } -
+
Loading…
@@ -98,15 +119,16 @@ header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
http://scratch.mit.edu/projects/
- +
-
+
+
diff --git a/index.html b/index.html index c90f3e2..3fa4be0 100755 --- a/index.html +++ b/index.html @@ -8,7 +8,7 @@ - + diff --git a/js/IO.js b/js/IO.js index bcb336b..29e40dd 100644 --- a/js/IO.js +++ b/js/IO.js @@ -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 + }); }); }; diff --git a/js/Interpreter.js b/js/Interpreter.js index c7bbee9..83cdd11 100644 --- a/js/Interpreter.js +++ b/js/Interpreter.js @@ -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'); + this.el.append('
'+(this.target==='Stage'?'':this.target+' ')+this.listName); + var c = this.containerEl = $('
').appendTo(this.el).width(this.width-13).height(this.height-34); + var s = this.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('
+'); // disabled because it doesn't do anything even in the original + this.el.append('
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){ + $('
').appendTo(c).append('
'+(i+1),'
'+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); +}; + diff --git a/js/primitives/VarListPrims.js b/js/primitives/VarListPrims.js index 065be7d..a8f734a 100644 --- a/js/primitives/VarListPrims.js +++ b/js/primitives/VarListPrims.js @@ -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; + } + } +}; diff --git a/player.css b/player.css index f64944b..d8797e6 100644 --- a/player.css +++ b/player.css @@ -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; -} +} \ No newline at end of file