Getting Started
The Sandbox programming model is designed to leverage your existing
web development skills. Here we explain some of the finer points of our test container.
The Sandbox philosophy is to make the security model as
unobtrusive as possible. As such, we focused on modeling the environment on top
of traditional HTML documents. By basing our approach on existing web
development methodologies, we are able to avoid writing an SDK. Instead, we will focus on the finer points of the security model and constraints of the technology.
Next to each sample, we included a “Try it” button. This will open
a new window executing the sample within the Sandbox. Within the Sandbox
experiment page you can create multiple instances of the same or unique code
and they all will stay isolated.
We also have sample pages that are already executing very
simple code samples. Our challenge to you is to find a way to break out of the
Sandbox to access or break the surrounding environment. Also, try to break the
isolation boundaries by creating conflicts across multiple gadgets. Ensuring security
is properly designed is a huge challenge and we can’t do it properly without your
help.
Also, we expect the documentation to be living documents
with frequent updates and additions. Please use our discussion forums to ask
questions, provide feedback, or suggest topics we should cover.
Hello World
Since all tutorials start with a canonical Hello World example, below is your
Hello World HTML document:
You will quickly notice that in most cases, your code will
not only run safely within our Sandbox, but also will run properly as a
stand-alone web page. This allows you to leverage your favorite tools for
development, debug your page against the browser, and in most cases, feel comfortable
the code will run unmodified within the Sandbox.
Let's have more fun
This next demo is a DHTML-based game. This is a great example to see the how the Sandbox goes well beyond just providing security. All gadgets
automatically support cloning as well as being paused and then later resumed. When you run this sample, be sure to try both of these features.
<HTML>
<TITLE>DHTML-Tris</TITLE>
<link href="http://www.websandbox.org/Images/favicon.ico" rel="icon" />
<STYLE type="text/css">
.blue {background: navy; color:red;font-weight:bolder}
span.KEY {color:red; font-weight:bolder}
.gameCell {width:15px;height:15px}
</STYLE>
<BODY onload="hookup()">
<script language="text/javascript">
var tm = null; // timer reference
function SetupGame() {
this.newGame=true; // flag that game is starting
this.rows=12; // number of rows in grid
this.cols=10; // number of cols in grid
this.curPos = 0; // current position of current piece
this.colOffset = 0; // min-offset for piece
this.maxOffset = 5; // max-offset for piece
this.startLevel = 1; // start level for game
// array of colors for each piece. Add more color values if desired
this.colorList = new Array("red","blue","green");
this.bgColor = "black"; // board background color
this.colorCurrent = this.colorList[0]; // color of current piece
this.score = 0; // current score
this.inprocess=false; // flag if game is in-process
this.shape = new Array(); // Array for current shape
this.shapeList = new Array(); // Array of available shapes
this.defaults = new Array(); // Defaults
this.loaded = false;
// These define the positions in a 5x5 grid that
// create the 4 default shapes
this.defaults[0] = new Array(10,11,12,13); // bar
this.defaults[1] = new Array(7,11,12,13); // t
this.defaults[2] = new Array(8,11,12,13); // right L
this.defaults[3] = new Array(6,11,12,13); // left L
this.defaults[4] = new Array(6,7,11,12);
this.defaults[5] = new Array(7,8,11,12);
this.defaults[6] = new Array(6,7,12,13);
this.shapeList = this.defaults; // start with the default shapes
// default speeds for each level
this.speeds = new Array(1500,1400,1300,1200,1100,1000,900,800,700,600);
this.level=this.startLevel; // current level
this.last=0; // last output position of current piece
this.speed = this.speeds[this.startLevel]; // current game speed
this.temp = this.speed; // temp speed for individual piece-used for space-bar
this.name = "DHTMLTris" // game name
this.keys = new Object();
this.keys.rotleft=73; // I
this.keys.rotright=75; // K
this.keys.left=74; // J
this.keys.right=76; // L
this.keys.drop=32; // <space>
this.keys.boss =66; // B
}
// initialize the game array.
window.game = new SetupGame();
var table = new Array(game.rows*game.cols);
var colors = new Array(game.rows*game.cols);
function getColOffset(s){
game.colOffset = 5;
game.maxOffset = 0;
var col=0;
for (var intLoop=0;intLoop<s.length;intLoop++) {
col = s[intLoop]%5;
if (game.colOffset>col) game.colOffset= col;
if (game.maxOffset<col) game.maxOffset = col;
}
game.maxOffset = 5-game.maxOffset;
}
function transform(dir) {
// do a 5x5 grid transform
var tempShape = new Array(game.shape.length), shapeRow=0, newLast=5;
for (var i=0;i<game.shape.length;i++) {
shapeRow = Math.floor((game.shape[i])/5);
if ("left"==dir)
tempShape[i] = ((4-((game.shape[i])%5))*5)+shapeRow;
else
tempShape[i] = ((game.shape[i])%5)*5+(4-shapeRow);
shapeRow = Math.floor(tempShape[i]/5);
if (newLast>shapeRow);
newLast=shapeRow;
}
paintIt(0,game.bgColor);
for (var i=0;i<game.shape.length;i++)
game.shape[i] = tempShape[i];
game.last=newLast;
getColOffset(game.shape);
paintIt(0,game.colorCurrent);
}
function setText(where,value) {
// output new text
var tr = document.getElementById(where);
tr.innerText = value;
}
function outScore() {
game.level = Math.ceil((game.score+1)/10);
game.speed = (game.level-1<game.speeds.length) ? game.speeds[game.level-1] : 350;
setText("score",game.score); setText("level",game.level);
}
function getOffset(pos,amount) {
return game.curPos+((pos)%5)+amount-((Math.floor(pos/5)-game.last)*game.cols) - game.colOffset;
}
function check(offset) {
// verify the next position
var block = false, nextpos=0;
for (var i=game.shape.length-1;i>=0;i--) {
nextpos = getOffset(game.shape[i],offset);
if ((nextpos>=(game.rows)*game.cols) || (colors[nextpos])) {
block=true;
break;
}
}
if (block) {
if (1==arguments.length)
for (var i=game.shape.length-1;i>=0;i--) {
nextpos = getOffset(game.shape[i],0);
if (nextpos >=0)
colors[nextpos] = true;
else {
clearTimeout(tm);
game.inprocess=false; // game over
}
}
}
return block;
}
function boundary(offset) {
// don't move left or right across board boundaries
var nextpos=0, startCol=game.cols, endCol=0;
for (var i=game.shape.length-1;i>=0;i--) {
nextpos = (game.curPos%game.cols)+((game.shape[i])%5) - game.colOffset;
if (nextpos<startCol) startCol=nextpos;
if (nextpos>endCol) endCol=nextpos;
}
return (startCol+offset>=0) && (endCol+offset<game.cols);
}
function paintIt(amount,color) {
var intOffset;
for (var i=game.shape.length-1;i>=0;i--) {
intOffset = getOffset(game.shape[i],amount);
if (intOffset>=0)
table[intOffset].bgColor = color;
}
}
function offset(amount) {
paintIt(0,game.bgColor);
paintIt(amount,game.colorCurrent);
game.curPos+=amount;
}
function checkRows() {
// check for completed row
var noRow=true;
var gotit=0;
var count=0;
var startcell;
for (var row=0;row<game.rows;row++) {
noRow=false;
startcell=count;
for (var cell=0;cell<game.cols;cell++) {
if (!colors[count])
noRow=true;
count++;
}
if (!noRow) {
gotit++;
game.score++;
for (var kill=startcell;kill<startcell+game.cols;kill++) {
colors[kill] = false;
table[kill].bgColor=game.bgColor;
}
for (var kill=startcell-1;kill>=0;kill--) {
colors[kill+game.cols] = colors[kill];
table[kill+game.cols].bgColor=table[kill].bgColor;
}
}
}
if (gotit>0) {
if (gotit>1)
game.score+=gotit; // give bonus
outScore();
}
}
function selectShape() {
clearTimeout(tm);
var newShape=Math.ceil(Math.random()*game.shapeList.length);
game.colorCurrent = game.colorList[Math.floor(Math.random()*game.colorList.length)];
game.curPos = (Math.ceil(Math.random()*(game.cols-5)));
game.newGame=false;
checkRows();
game.shape = game.shapeList[newShape-1];
getColOffset(game.shape);
game.temp = game.speed;
paintIt(0,game.colorCurrent);
}
document.body.onkeypress = function(ev)
{
if (game.inprocess)
ev.preventDefault();
}
document.body.onkeydown = function(ev) {
// process keyboard
var key = ev.keyCode;
if (game.inprocess) {
if (game.keys.left==key)
if ((!check(-1,false)) && boundary(-1))
offset(-1);
if (game.keys.right==key)
if ((!check(1,false)) && boundary(1))
offset(1);
if (game.keys.rotleft==key) transform("left");
if (game.keys.rotright==key) transform("right");
if (game.keys.drop==key) {
clearTimeout(tm);
game.temp = 5;
tm=setTimeout(doTimer,game.temp);
}
ev.preventDefault();
}
}
function doTimer() {
var block = false;
if (check(game.cols))
selectShape(); // get new piece
else
offset(game.cols); // move down 1 row
if (game.inprocess)
{
if (tm)
clearTimeout(tm);
tm=setTimeout(doTimer,game.temp);
}
else
if(!game.inprocess) {
clearTimeout(tm);
alert("Game Over!");
setText("comment","Game Over!");
}
}
function cleanup() {
game.shapeList = new Array();
game.shapeList = game.defaults;
game.newGame=true;
for (var counter=0;counter<game.rows*game.cols;counter++) {
table[counter].bgColor = game.bgColor;
colors[counter] = false;
}
game.curPos = game.last = game.score = 0;
game.level = game.startLevel;
game.speed = game.speeds[game.startLevel-1];
game.inprocess=true;
outScore();
selectShape();
doTimer();
}
function reset(sDoc,sWin) {
var tables = sDoc.getElementsByTagName("TABLE");
game.defaults = new Array();
for (var intTable = 0; intTable< tables.length; intTable++)
if ("shape"==tables[intTable].className) {
temp = sWin.getSelectedShape(tables[intTable]);
if (0<temp.length) {
var p = game.defaults[game.defaults.length] = new Array();
for (var intCopy=0;intCopy<temp.length;intCopy++)
p[intCopy] = parseInt(temp[intCopy]);
}
}
}
function numberOrder(a,b) {return a-b}
function hookup() {
if (!game.loaded) {
var counter=0;
var elTable = document.createElement("table");
elTable.style.backgroundColor = "black";
var elBody = document.createElement("tbody");
elTable.appendChild(elBody);
for (var irow=0;irow<game.rows;irow++) {
var elRow = document.createElement("tr");
elBody.appendChild(elRow);
for (var icol=0;icol<game.cols;icol++)
{
var tCell = document.createElement("td");
tCell.innerHTML = " ";
tCell.className = "gameCell"
elRow.appendChild(tCell);
table[counter] = tCell;
counter++;
}
}
document.getElementById("boardContainer").appendChild(elTable);
var overlay = document.getElementById("overlay");
var bt = document.getElementsByTagName("INPUT");
for (var intloop=0;intloop<bt.length;intloop++)
if ("button"==bt[intloop].type) {
bt[intloop].onmouseenter = function() {this.className='blue'};
bt[intloop].onmouseleave =function() {this.className=''};
}
}
game.loaded=true;
}
</script>
<div align="center">
<table cellpadding="5"><tr><td valign="top" align="center">
<table bordercolor="lightgrey" bgcolor="lightgrey"><tr><td align="center">
<h1 style="color:darkblue">DHTML-tris</h1>
<P style="margin-bottom:0" title="Game gets harder at higher levels!"> <B>Level:</B>
<span id="level"> 1 </span></P>
<P style="margin-top:0" title="Clear more than 1 row at a time for bonus!">
<B>Score:</B> <span id="score"> 0 </span>
<P style="margin-top:2px;color:navy;letter-spacing:2;font-weight:bolder"
id="comment"> </P>
<FORM>
<input type="button" value="New Game" id="start" style="width:150px" onclick="cleanup();this.blur()"><BR>
</FORM>
</td></tr></table>
</td><td>
<table border="1" title="Game Board" id="board"><tr><td id="boardContainer"><textarea style="outline:none;resize: none;border: none;background: transparent; overflow:hidden; position:absolute;width:300px;height:300px" id="overlay"></textarea></td></tr></table><em>Click on the board to activate</em>
</td></tr>
<TR><TD COLSPAN="2" align="center">
<TABLE TITLE="Keyboard Help" BORDERCOLOR="lightgrey" bgcolor="lightgrey" width="100%">
<TR><TD align="center" style="font-size:75%; color:navy">
<SPAN CLASS="KEY">J</SPAN> - Move Left, <SPAN CLASS="KEY">L</SPAN> - Move Right, <SPAN CLASS="KEY">I</SPAN> - Rotate Left, <SPAN CLASS="KEY">K</SPAN> - Rotate Right<BR>
<SPAN CLASS="KEY">(space)</SPAN> - Drop
</TD></TR></TABLE>
</TD></TR>
</table>
</div>
</BODY>
</HTML>