// Append ?raw to the URL of this page to view the raw source (for 
// comments, copyright & licence), or ?pretty for a colorized version.

BlockDudeSkinPreloader=function(skin){var urlKeys=["wall","block","door","background","dudeLeft","dudeRight","dudeLeftHolding","dudeRightHolding","victoryDude","victoryDudeLeft","victoryDudeRight"];var images=[];for(var i=0;i<urlKeys.length;i++){if(skin[urlKeys[i]]){var image=new Image();image.src=skin[urlKeys[i]];images.push(image);}}
this.images=images;};MochiKit.DOM.registerDOMConverter("Object.domNode",function(o){return!!o.domNode;},function(o){return o.domNode;});BlockDudeInterface=function(levelSets,skinSet){log("BlockDudeInterface constructor called.");var self=this;var controls=new BlockDudeControls(null);var loader,skin,gameInstance;var levelSet=null;var disableInput=false;var levelSets=levelSets||BlockDudeLevels;var skinSet=skinSet||BlockDudeSkins;var levelTabs=new TabPanel();for(var i=0;i<levelSets.length;i++){var ls=new BDLevelSelector(levelSets[i].list);ls.onfocus=function(){levelSet=this;};ls.onlevelselect=function(){if(disableInput)return;disableInput=true;levelSet=this;newGame(this.selectedLevel.levelObj);showGamePanel();};levelTabs.addPanel(new TabPanelPanel(levelSets[i].tab,ls));}
var selectorPanel=DIV({"class":"BDSelectorPanel"},H2(null,"Pick a level"),levelTabs);var gamePanel=DIV({"class":"BlockDudeGamePanel"});hideElement(gamePanel);this.domNode=DIV({"class":"BlockDudeUI"},controls,gamePanel,selectorPanel);new Fader(selectorPanel,0,1,300);function setSkin(newSkin){skin=newSkin;loader=new BlockDudeSkinPreloader(skin);};var controlsMapping={undo:null,reset:null,left:function(){levelTabs.showPreviousPanel();},right:function(){levelTabs.showNextPanel();},raise:function(){levelSet.focusPrevious();},drop:function(){levelSet.focusNext();},orientLeft:null,orientRight:null,select:function(){levelSet.selectedLevel.open();}};controls.gameInstance=controlsMapping;function showGamePanel(){new Fader(selectorPanel,1,0,300,function(){hideElement(selectorPanel)
disableInput=false;new Fader(gamePanel,0,1,300);showElement(gamePanel);controls.showEndGameButton();});};function showSelectionPanel(){new Fader(gamePanel,1,0,300,function(){hideElement(gamePanel);removeElement(gameInstance.domNode);gameInstance=null;controls.hideEndGameButton();controls.gameInstance=controlsMapping;new Fader(selectorPanel,0,1,300);showElement(selectorPanel);});};function newGame(levelObj){gameInstance=new BlockDude(levelObj,skin);gameInstance.ongameend=gameEnd;gameInstance.onvictory=gameVictory;gamePanel.appendChild(gameInstance.domNode);log("New gameInstance",gameInstance);controls.gameInstance=gameInstance;log("controls.gameInstance =",controls.gameInstance);};function gameVictory(gameInstance){levelSet.levelBeaten(gameInstance.level);};function gameEnd(){function hide(){window.setTimeout(function(){showSelectionPanel();},1000);};if(gameInstance.result==="victory"){gameInstance.onvictoryvisible=hide;}else{hide();}};this.toString=function(){return"[BlockDudeInterface]";};setSkin(skinSet[0]);window.unlock=function(){for(var i=0;i<levelTabs.panels.length;i++){var panelElements=levelTabs.panels[i].panel.elements;for(var j=0;j<panelElements.length;j++){panelElements[j].unlock();}}
log("Cheater!",self);};log("BlockDudeInterface initialization complete.",self);};BlockDudeControls=function(noKeyCapture,gameInstance,showEndGameButton){var self=this;this.gameInstance=gameInstance;var endGameButton=INPUT({"type":"button","class":"BDButtonEndGame","value":"End Game"});var buttons={};var tabOrder=["reset","raise","undo","left","right","orientLeft","drop","orientRight"];var titles={undo:"Undo last move",reset:"Reset the board",left:"Move left",right:"Move right",raise:"Pick up a block",drop:"Drop a block",orientLeft:"Face left",orientRight:"Face right"};var captions={};function condCall(methodName){var f=function(){if(self.gameInstance&&self.gameInstance[methodName]){log("calling: "+methodName);self.gameInstance[methodName]();}};return f;};var funcs={undo:function(){alert("Undo doesn't work yet.\nSorry.");},reset:function(){if(self.gameInstance&&self.gameInstance.reset&&confirm("Are you sure that you want to reset the board?"))
self.gameInstance.reset();},left:condCall("left"),right:condCall("right"),raise:condCall("raise"),drop:condCall("drop"),orientLeft:condCall("orientLeft"),orientRight:condCall("orientRight"),select:condCall("select")};for(var i=0;i<tabOrder.length;i++){var key=tabOrder[i];var cls="BDButton BDButton"+
key.substring(0,1).toUpperCase()+key.substring(1);var el=A({"class":cls,"title":(titles[key]||""),"href":"javascript:void(null);"},captions[key]||"");addEvent(el,"click",funcs[key]);buttons[key]=el;}
addEvent(endGameButton,"click",function(){if(self.gameInstance&&self.gameInstance.quit)
self.gameInstance.quit();});this.domNode=DIV({"class":"BDControlPad"},buttons.undo,buttons.reset,buttons.left,buttons.right,buttons.raise,buttons.drop,buttons.orientLeft,buttons.orientRight,endGameButton);var keyCodes={39:"right",37:"left",38:"raise",40:"drop",36:"reset",33:"undo",35:"orientLeft",34:"orientRight",100:"left",102:"right",104:"raise",98:"drop",97:"orientLeft",99:"orientRight",103:"undo",105:"reset",13:"select"};var altKeyCodes={39:"orientRight",37:"orientLeft"};function keyListener(evt){if(evt.ctrlKey||evt.shiftKey)return;var funcName=(evt.altKey)?altKeyCodes[evt.keyCode]:keyCodes[evt.keyCode];if(funcName){funcs[funcName]();evt.stopPropagation();evt.preventDefault();return false;}
return true;};this.removeKeyListener=function(){removeEvent(document,"keyup",keyListener);};this.setKeyListener=function(){addEvent(document,"keyup",keyListener);};this.hideEndGameButton=function(){addElementClass(endGameButton,"hidden");};this.showEndGameButton=function(){removeElementClass(endGameButton,"hidden");};if(!showEndGameButton)self.hideEndGameButton();if(!noKeyCapture)self.setKeyListener();};function BlockDude(level,skin){var self=this;var TRANSPARENT_IMAGE_URL="transparent.gif";function BoardElement(x,y,character){var self=this;this.WALL="Wall";this.BLOCK="Block";this.DUDE_START="Dude Start";this.DOOR="Door";this.CLEAR_SPACE="Clear Space";var src=TRANSPARENT_IMAGE_URL;var bgImage=null;this.type=this.CLEAR_SPACE;this.contains=null;switch(character){case level.clearSpace:break;case level.wall:this.type=this.WALL;src=skin.wall;break;case level.block:this.contains=this.BLOCK;src=skin.block;break;case level.dudeStart:this.type=this.DUDE_START;src=skin.dudeLeft;break;case level.door:this.type=this.DOOR;bgImage=skin.door;break;default:throw new Error("Invalid map character at "+x+","+y+".");}
this.domNode=IMG({"src":src,"style":{"width":skin.blockWidth+skin.blockWidthUnit,"height":skin.blockHeight+skin.blockHeightUnit,"left":skin.blockWidth*x+skin.blockWidthUnit,"top":skin.blockHeight*y+skin.blockHeightUnit}});if(bgImage)this.domNode.style.backgroundImage='url('+bgImage+')';this.x=x;this.y=y;};BoardElement.prototype.isEnterable=function(){return this.contains!=this.BLOCK&&this.type!=this.WALL;};BoardElement.prototype.makeBlock=function(){this.contains=self.BLOCK;};BoardElement.prototype.toString=function(){return"[BoardElement of type "+this.type+" at ("+this.x+", "+this.y+")]";};var dude={x:null,y:null,orientation:null,carrying:false}
var boardElements=[];function parseBoardLevel(){for(var y=0;y<level.board.length;y++){var row=level.board[y];var elArray=[];for(var x=0;x<row.length;x++){elArray[x]=new BoardElement(x,y,row[x],level);if(elArray[x].type==elArray[x].DUDE_START){dude.x=x;dude.y=y;dude.orientation=-1;}}
boardElements[y]=elArray;}
if(!dude.orientation){throw new Error("There is no dude start position set.");}}
parseBoardLevel();var board=DIV({"class":"blockDudeBoard","style":{"width":skin.blockWidth*level.width+"px","height":skin.blockHeight*level.height+"px","backgroundImage":"url("+skin.background+")"}},boardElements);self.domNode=DIV({"class":"blockDudeContainer","style":{"height":skin.blockHeight*level.height+"px"}},H2(null,level.name),board);function triggerEvent(name){if(self['on'+name]){self['on'+name](self);}};function endGame(){self.endTime=new Date();self.status="done";};function isJumpable(x,y){return boardElements[y-1]&&boardElements[y-1][x]&&boardElements[y-1][x].isEnterable();};function removeDude(){boardElements[dude.y][dude.x].domNode.src=TRANSPARENT_IMAGE_URL;if(dude.carrying){removeBlock(dude.x,dude.y-1);}};function removeBlock(x,y){var el=boardElements[y][x];el.domNode.src=TRANSPARENT_IMAGE_URL;el.contains=null;};function addBlock(x,y){var el=boardElements[y][x];el.domNode.src=skin.block;el.contains=el.BLOCK;};function setDudeImage(boardElement){boardElement=boardElement||boardElements[dude.y][dude.x];var src;if(dude.carrying){src=(dude.orientation==-1)?skin.dudeLeftHolding:skin.dudeRightHolding;}else{src=(dude.orientation==-1)?skin.dudeLeft:skin.dudeRight;}
boardElement.domNode.src=src;};function getColumnLocation(boardElement){if(boardElement&&!boardElement.isEnterable()){boardElement=boardElements[dude.y-1]&&boardElements[dude.y-1][dude.x+dude.orientation];}else{var elBelow=boardElement;while(true){elBelow=boardElements[elBelow.y+1]&&boardElements[elBelow.y+1][elBelow.x];if(elBelow&&elBelow.isEnterable()){boardElement=elBelow;}else{break;}}}
return boardElement;};function moveDude(){var newXPos=dude.x+dude.orientation;var newYPos=dude.y;log("moveDude: new X:",newXPos,"new Y:",newYPos);var be=getColumnLocation(boardElements[newYPos]&&boardElements[newYPos][newXPos]);if(!be||!be.isEnterable()){log("not enterable",be);return false;}
if(dude.carrying){if(!boardElements[be.y-1]||!boardElements[be.y-1][be.x].isEnterable()){log("overhaning block",be);return false;}}
removeDude();setDudeImage(be);if(dude.carrying){removeBlock(dude.x,dude.y-1);addBlock(be.x,be.y-1);}
dude.x=be.x;dude.y=be.y;if(be.type===be.DOOR){endGame();self.results="victory";triggerEvent("victory");triggerEvent("gameend");function victory(){if(skin.orientationSensitiveVictory){be.domNode.src=(dude.orientation==-1)?skin.victoryDudeLeft:skin.victoryDudeRight;}else{be.domNode.src=skin.victoryDude;}
if(skin.victoryDudeDuration){window.setTimeout(function(){triggerEvent("victoryvisible");},skin.victoryDudeDuration);}else{triggerEvent("victoryvisible");}}
if(skin.victoryDudeDelay){window.setTimeout(victory,skin.victoryDudeDelay);}else{victory();}}
return true;};function setScrollPos(xBoardElPos){};self.left=function(){log("left()",self);self.startIfNotStarted();if(self.status!=="playing")return false;dude.orientation=-1;return moveDude();};self.orientLeft=function(){log("orientLeft()",self);self.startIfNotStarted();if(self.status!=="playing")return false;dude.orientation=-1;setDudeImage();};self.right=function(){log("right()",self);self.startIfNotStarted();if(self.status!=="playing")return false;dude.orientation=1;return moveDude();};self.orientRight=function(){log("orientRight()",self);self.startIfNotStarted();if(self.status!=="playing")return false;dude.orientation=1;setDudeImage();};self.raise=function(){log("raise()",self);self.startIfNotStarted();if(self.status!=="playing")return false;var boardElement=boardElements[dude.y][dude.x+dude.orientation];var aboveEl=boardElements[dude.y-1]&&boardElements[dude.y-1][dude.x+dude.orientation];if(boardElement.contains!==boardElement.BLOCK)return false;if(boardElement&&aboveEl&&aboveEl.isEnterable()&&!dude.carrying){removeBlock(dude.x+dude.orientation,dude.y);addBlock(dude.x,dude.y-1);dude.carrying=true;setDudeImage();return true;}
return false;};self.drop=function(){log("drop()",self);self.startIfNotStarted();if(self.status!=="playing"||!dude.carrying)return false;var be=getColumnLocation(boardElements[dude.y][dude.x+dude.orientation]);if(be&&be.isEnterable()&&(boardElements[dude.y-1][dude.x+dude.orientation].isEnterable())){removeBlock(dude.x,dude.y-1);addBlock(be.x,be.y);dude.carrying=false;setDudeImage();return true;}
return false;};self.getTimePlaying=function(){if(!self.startTime)throw new Error("Game has not started yet.");var end=self.endTime?self.endTime.getTime():(new Date()).getTime();return end-self.startTime.getTime();};self.startIfNotStarted=function(){if(self.status=="done")return false;if(self.status!=="playing"){triggerEvent("start");self.startTime=new Date();self.status="playing";}
return true;};self.quit=function(){endGame();if(!self.results){self.results="loss";triggerEvent("loss");triggerEvent("gameend");}};self.reset=function(){while(board.firstChild){board.removeChild(board.firstChild);}
boardElements=[];parseBoardLevel();appendChildNodes(board,boardElements);};self.toString=function(){return"[BlockDude instance: \""+self.level.name+"\", "+self.status+"]";};self.skin=skin;self.level=level;self.status="uninitialized";self.results=null;self.boardWidth=skin.blockWidth*level.width;self.boardHeight=skin.blockHeight*level.height;};function BDLevelSelector(levelArray){var self=this;this.toString=function(){return"[LevelSelector object containing levels "+map(function(l){return l.toString();},self.elements).join(",")+"]";}
this.elements=map(function(level){var se=new BDSelectorElement(level);se.onfocus=function(){if(self.selectedLevel&&self.selectedLevel!=se)self.selectedLevel.blur();self.selectedLevel=se;}
se.onselect=function(){if(self.onlevelselect)self.onlevelselect(se);};return se;},levelArray);if(this.elements[0])this.elements[0].unlock(true);this.domNode=UL(null,this.elements);this.levelBeaten=function(levelObj){for(var i=0;i<self.elements.length;i++){if(self.elements[i].levelObj===levelObj){if(self.elements[i+1]){self.elements[i+1].unlock();self.elements[i+1].focus();}
break;}}};this.selectedLevel=null;this.selectionIndex=null;this.onlevelselect=null;this.onfocus=null;this.onblur=null;function triggerEvent(name){if(self["on"+name])self["on"+name](self);};this.focusNext=function(){self.focusByDelta(1);};this.focusByDelta=function(delta){var ni=self.selectionIndex+delta;var el=self.elements[ni]
log("focusByDelta("+delta+") called:",ni,el);if(el){el.focus();self.selectionIndex=ni;}};this.focusPrevious=function(){self.focusByDelta(-1);};this.openFocused=function(){self.selectedLevel.open();};this.focus=function(){if(!self.selectedLevel&&self.elements[0]){self.elements[0].focus();}
triggerEvent("focus");};this.blur=function(){triggerEvent("blur");};};function BDSelectorElement(levelObj,levelSelector){var self=this;this.levelObj=levelObj;this.unlocked=false;this.levelSelector=levelSelector;this.passHidden=true;var passNode=levelObj.password?P({"class":"password hidden"},"Password: "):null;var passField=INPUT({"type":"password","size":20,"class":"field"});var passEsc=INPUT({"type":"button","value":"Cancel","class":"esc"})
var passForm=FORM({"method":"GET","action":"./","class":"hidden"},P({"class":"BDSEPassContainer"},LABEL(null,"Password: ",passField)," ",INPUT({"type":"submit","value":"Go","class":"submit"})," ",passEsc));this.domNode=LI({"class":"BDSelectorElement locked"},P({"class":"title"},SPAN(null,levelObj.name)," by ",levelObj.author),passForm,passNode);this.open=function(){self.focus();if(self.unlocked){if(!self.unlocked)alert("Level unlocked.");if(self.onselect)self.onselect();}else{showPassForm();}};this.tryPass=function(pass){log("tryPass(",pass,")",self);if(pass==levelObj.password){self.unlock();self.open();return true;}else{log("Password",pass,"failed.");return false;}};function showPassForm(){log("showPassForm()",self);passForm.firstChild.firstChild.firstChild.nodeValue="Password: ";removeElementClass(passForm,'hidden');passField.focus();}
function hidePassForm(){log("hidePassForm()",self);passField.blur();passEsc.blur();passForm.firstChild.firstChild.childNodes[1].blur();passField.value="";addElementClass(passForm,'hidden');}
function showPass(){if(!passNode||!this.passHidden)return;appendChildNodes(passNode,self.levelObj.password);removeElementClass(passNode,"hidden");this.passHidden=false;};this.unlock=function(dontShowPass){self.unlocked=true;removeElementClass(self.domNode,"locked");if(!dontShowPass)showPass();log("Unlocked:",self);};this.focus=function(){log("Focused:",self);addElementClass(self.domNode,"focused");if(self.onfocus)self.onfocus(self);};this.blur=function(){log("Blurred:",self);hidePassForm();removeElementClass(self.domNode,"focused");if(self.onblur)self.onblur(self);};this.toString=function(){var l=self.unlocked?"unlocked":"locked";return"[BDSelectorElement containing level \""+levelObj.name+"\" ("+l+")]";};this.onfocus=null;this.onblur=null;this.onselect=null;addEvent(this.domNode,"click",this.open);addEvent(passForm,"keyup",function(evt){if(evt.keyCode==27){hidePassForm();}
evt.stopPropagation();});addEvent(passForm,"submit",function(evt){if(self.tryPass(passField.value)){hidePassForm();}else{passForm.firstChild.firstChild.firstChild.nodeValue="Try again: ";passField.focus();}
return false;});addEvent(passEsc,"click",function(evt){evt.stopPropagation();hidePassForm();});addEvent(passForm,"click",function(evt){evt.stopPropagation();});};function TabPanel(){var self=this;var tabset=DIV({"class":"TabPanelTabset"});var panelset=DIV({"class":"TabPanelPanelset"});this.domNode=DIV({"class":"TabPanel"},tabset,panelset);this.panels=[];this.addPanel=function(panelObj){self.panels.push(panelObj);tabset.appendChild(panelObj.tabDomNode);panelset.appendChild(panelObj.panelDomNode);panelObj.onshow=function(){if(self.currentPanel&&self.currentPanel!=panelObj)self.currentPanel.hide();self.currentPanel=panelObj;self.currentPanelIndex=(function(){for(var i=0;i<self.panels.length;i++){if(self.panels[i]==panelObj)return i;}})();};if(!self.currentPanel)panelObj.show();};this.currentPanel=null;this.showPanel=function(panelID){if(typeof(panelID)=='numer'){if(self.panels[i]){self.panels[i].show();return true;}else{throw new Error("There is no panel at index "+panelID);}}else{for(var i=0;i<self.panels.length;i++){if(self.panels[i]==panelID){self.panels[i].show();return true;}}}};this.showPanelByDelta=function(delta){log("showPanelByDelta(",delta,") called.  self.currentPanelIndex = ",self.currentPanelIndex);var el=self.panels[self.currentPanelIndex+delta];if(el)el.show();log("Current panel:"+self.currentPanel,"Index:",self.currentPanelIndex);};this.showPreviousPanel=function(){self.showPanelByDelta(-1);};this.showNextPanel=function(){self.showPanelByDelta(1);};for(var i=0;i<arguments.length;i++){this.addPanel(arguments[i]);}
if(arguments.length)showPanel(0);};function TabPanelPanel(tabTitle,panel){var self=this;this.tabDomNode=A({"class":"TabPanelTab","href":"javascript:void(null);"},tabTitle);this.tabText=(typeof(tabTitle=="string"))?tabTitle:scrapeText(this.tabDomNode);this.panel=panel;this.panelDomNode=DIV({"class":"TabPanelPanel"},panel);this.show=function(){addElementClass(self.tabDomNode,"TabPanelCurrentTab");addElementClass(self.panelDomNode,"TabPanelCurrentPanel");self.focus();if(self.onshow)self.onshow();};this.hide=function(){removeElementClass(self.tabDomNode,"TabPanelCurrentTab");removeElementClass(self.panelDomNode,"TabPanelCurrentPanel");self.blur();if(self.onhide)self.onhide();};this.focus=function(){if(self.panel.focus)self.panel.focus();if(self.onfocus)self.onfocus();};this.blur=function(){if(self.panel.blur)self.panel.blur();if(self.onblur)self.onblur();};addEvent(this.tabDomNode,"click",function(){self.show();return false;});this.onshow=null;this.onhide=null;this.onfocus=null;this.onblur=null;};TabPanelPanel.prototype.toString=function(){return"[TabPanelPanel \""+this.tabText+"\"]";};