diff --git a/src/blockrain.jquery.libs.js b/src/blockrain.jquery.libs.js
deleted file mode 100644
index 21eb819..0000000
--- a/src/blockrain.jquery.libs.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// jQuery Widget
-(function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)})(function(e){var t=0,i=Array.prototype.slice;e.cleanData=function(t){return function(i){var s,n,a;for(a=0;null!=(n=i[a]);a++)try{s=e._data(n,"events"),s&&s.remove&&e(n).triggerHandler("remove")}catch(o){}t(i)}}(e.cleanData),e.widget=function(t,i,s){var n,a,o,r,h={},l=t.split(".")[0];return t=t.split(".")[1],n=l+"-"+t,s||(s=i,i=e.Widget),e.expr[":"][n.toLowerCase()]=function(t){return!!e.data(t,n)},e[l]=e[l]||{},a=e[l][t],o=e[l][t]=function(e,t){return this._createWidget?(arguments.length&&this._createWidget(e,t),void 0):new o(e,t)},e.extend(o,a,{version:s.version,_proto:e.extend({},s),_childConstructors:[]}),r=new i,r.options=e.widget.extend({},r.options),e.each(s,function(t,s){return e.isFunction(s)?(h[t]=function(){var e=function(){return i.prototype[t].apply(this,arguments)},n=function(e){return i.prototype[t].apply(this,e)};return function(){var t,i=this._super,a=this._superApply;return this._super=e,this._superApply=n,t=s.apply(this,arguments),this._super=i,this._superApply=a,t}}(),void 0):(h[t]=s,void 0)}),o.prototype=e.widget.extend(r,{widgetEventPrefix:a?r.widgetEventPrefix||t:t},h,{constructor:o,namespace:l,widgetName:t,widgetFullName:n}),a?(e.each(a._childConstructors,function(t,i){var s=i.prototype;e.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete a._childConstructors):i._childConstructors.push(o),e.widget.bridge(t,o),o},e.widget.extend=function(t){for(var s,n,a=i.call(arguments,1),o=0,r=a.length;r>o;o++)for(s in a[o])n=a[o][s],a[o].hasOwnProperty(s)&&void 0!==n&&(t[s]=e.isPlainObject(n)?e.isPlainObject(t[s])?e.widget.extend({},t[s],n):e.widget.extend({},n):n);return t},e.widget.bridge=function(t,s){var n=s.prototype.widgetFullName||t;e.fn[t]=function(a){var o="string"==typeof a,r=i.call(arguments,1),h=this;return a=!o&&r.length?e.widget.extend.apply(null,[a].concat(r)):a,o?this.each(function(){var i,s=e.data(this,n);return"instance"===a?(h=s,!1):s?e.isFunction(s[a])&&"_"!==a.charAt(0)?(i=s[a].apply(s,r),i!==s&&void 0!==i?(h=i&&i.jquery?h.pushStack(i.get()):i,!1):void 0):e.error("no such method '"+a+"' for "+t+" widget instance"):e.error("cannot call methods on "+t+" prior to initialization; "+"attempted to call method '"+a+"'")}):this.each(function(){var t=e.data(this,n);t?(t.option(a||{}),t._init&&t._init()):e.data(this,n,new s(a,this))}),h}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{disabled:!1,create:null},_createWidget:function(i,s){s=e(s||this.defaultElement||this)[0],this.element=e(s),this.uuid=t++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=e(),this.hoverable=e(),this.focusable=e(),s!==this&&(e.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===s&&this.destroy()}}),this.document=e(s.style?s.ownerDocument:s.document||s),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this.options=e.widget.extend({},this.options,this._getCreateOptions(),i),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(t,i){var s,n,a,o=t;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof t)if(o={},s=t.split("."),t=s.shift(),s.length){for(n=o[t]=e.widget.extend({},this.options[t]),a=0;s.length-1>a;a++)n[s[a]]=n[s[a]]||{},n=n[s[a]];if(t=s.pop(),1===arguments.length)return void 0===n[t]?null:n[t];n[t]=i}else{if(1===arguments.length)return void 0===this.options[t]?null:this.options[t];o[t]=i}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled",!!t),t&&(this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus"))),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(t,i,s){var n,a=this;"boolean"!=typeof t&&(s=i,i=t,t=!1),s?(i=n=e(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),e.each(s,function(s,o){function r(){return t||a.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof o?a[o]:o).apply(a,arguments):void 0}"string"!=typeof o&&(r.guid=o.guid=o.guid||r.guid||e.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+a.eventNamespace,u=h[2];u?n.delegate(u,l,r):i.bind(l,r)})},_off:function(t,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,t.unbind(i).undelegate(i),this.bindings=e(this.bindings.not(t).get()),this.focusable=e(this.focusable.not(t).get()),this.hoverable=e(this.hoverable.not(t).get())},_delay:function(e,t){function i(){return("string"==typeof e?s[e]:e).apply(s,arguments)}var s=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,i,s){var n,a,o=this.options[t];if(s=s||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(e.isFunction(o)&&o.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,i){e.Widget.prototype["_"+t]=function(s,n,a){"string"==typeof n&&(n={effect:n});var o,r=n?n===!0||"number"==typeof n?i:n.effect||i:t;n=n||{},"number"==typeof n&&(n={duration:n}),o=!e.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),o&&e.effects&&e.effects.effect[r]?s[t](n):r!==t&&s[r]?s[r](n.duration,n.easing,a):s.queue(function(i){e(this)[t](),a&&a.call(s[0]),i()})}}),e.widget});
\ No newline at end of file
diff --git a/src/blockrain.jquery.src.js b/src/blockrain.jquery.src.js
deleted file mode 100644
index 7fa74e0..0000000
--- a/src/blockrain.jquery.src.js
+++ /dev/null
@@ -1,1578 +0,0 @@
-((function ( $ ) {
-
- "use strict";
-
- $.widget('aerolab.blockrain', {
-
- options: {
- autoplay: false, // Let a bot play the game
- autoplayRestart: true, // Restart the game automatically once a bot loses
- showFieldOnStart: true, // Show a bunch of random blocks on the start screen (it looks nice)
- theme: null, // The theme name or a theme object
- blockWidth: 10, // How many blocks wide the field is (The standard is 10 blocks)
- autoBlockWidth: false, // The blockWidth is dinamically calculated based on the autoBlockSize. Disabled blockWidth. Useful for responsive backgrounds
- autoBlockSize: 24, // The max size of a block for autowidth mode
- difficulty: 'normal', // Difficulty (normal|nice|evil).
- speed: 20, // The speed of the game. The higher, the faster the pieces go.
- asdwKeys: true, // Enable ASDW keys
-
- // Copy
- playText: 'Let\'s play some Tetris',
- playButtonText: 'Play',
- gameOverText: 'Game Over',
- restartButtonText: 'Play Again',
- scoreText: 'Score',
-
- // Basic Callbacks
- onStart: function(){},
- onRestart: function(){},
- onGameOver: function(score){},
-
- // When a block is placed
- onPlaced: function(){},
- // When a line is made. Returns the number of lines, score assigned and total score
- onLine: function(lines, scoreIncrement, score){}
- },
-
-
- /**
- * Start/Restart Game
- */
- start: function() {
- this._doStart();
- this.options.onStart.call(this.element);
- },
-
- restart: function() {
- this._doStart();
- this.options.onRestart.call(this.element);
- },
-
- gameover: function() {
- this.showGameOverMessage();
- this._board.gameover = true;
- this.options.onGameOver.call(this.element, this._filled.score);
- },
-
- _doStart: function() {
- this._filled.clearAll();
- this._filled._resetScore();
- this._board.cur = this._board.nextShape();
- this._board.started = true;
- this._board.gameover = false;
- this._board.dropDelay = 5;
- this._board.render(true);
- this._board.animate();
-
- this._$start.fadeOut(150);
- this._$gameover.fadeOut(150);
- this._$score.fadeIn(150);
- },
-
-
- pause: function() {
- this._board.paused = true;
- },
-
- resume: function() {
- this._board.paused = false;
- },
-
- autoplay: function(enable) {
- if( typeof enable !== 'boolean' ){ enable = true; }
-
- // On autoplay, start the game right away
- this.options.autoplay = enable;
- if( enable && ! this._board.started ) {
- this._doStart();
- }
- this._setupControls( ! enable );
- this._setupTouchControls( ! enable );
- },
-
- controls: function(enable) {
- if( typeof enable !== 'boolean' ){ enable = true; }
- this._setupControls(enable);
- },
-
- touchControls: function(enable) {
- if( typeof enable !== 'boolean' ){ enable = true; }
- this._setupTouchControls(enable);
- },
-
- score: function(newScore) {
- if( typeof newScore !== 'undefined' && parseInt(newScore) >= 0 ) {
- this._filled.score = parseInt(newScore);
- this._$scoreText.text(this._filled_score);
- }
- return this._filled.score;
- },
-
- freesquares: function() {
- return this._filled.getFreeSpaces();
- },
-
- showStartMessage: function() {
- this._$start.show();
- },
-
- showGameOverMessage: function() {
- this._$gameover.show();
- },
-
- /**
- * Update the sizes of the renderer (this makes the game responsive)
- */
- updateSizes: function() {
-
- this._PIXEL_WIDTH = this.element.innerWidth();
- this._PIXEL_HEIGHT = this.element.innerHeight();
-
- this._BLOCK_WIDTH = this.options.blockWidth;
- this._BLOCK_HEIGHT = Math.floor(this.element.innerHeight() / this.element.innerWidth() * this._BLOCK_WIDTH);
-
- this._block_size = Math.floor(this._PIXEL_WIDTH / this._BLOCK_WIDTH);
- this._border_width = 2;
-
- // Recalculate the pixel width and height so the canvas always has the best possible size
- this._PIXEL_WIDTH = this._block_size * this._BLOCK_WIDTH;
- this._PIXEL_HEIGHT = this._block_size * this._BLOCK_HEIGHT;
-
- this._$canvas .attr('width', this._PIXEL_WIDTH)
- .attr('height', this._PIXEL_HEIGHT);
- },
-
-
- theme: function(newTheme){
-
- if( typeof newTheme === 'undefined' ) {
- return this.options.theme || this._theme;
- }
-
- // Setup the theme properly
- if( typeof newTheme === 'string' ) {
- this.options.theme = newTheme;
- this._theme = $.extend(true, {}, BlockrainThemes[newTheme]);
- }
- else {
- this.options.theme = null;
- this._theme = newTheme;
- }
-
- if( typeof this._theme === 'undefined' || this._theme === null ) {
- this._theme = $.extend(true, {}, BlockrainThemes['retro']);
- this.options.theme = 'retro';
- }
-
- if( isNaN(parseInt(this._theme.strokeWidth)) || typeof parseInt(this._theme.strokeWidth) !== 'number' ) {
- this._theme.strokeWidth = 2;
- }
-
- // Load the image assets
- this._preloadThemeAssets();
-
- if( this._board !== null ) {
- if( typeof this._theme.background === 'string' ) {
- this._$canvas.css('background-color', this._theme.background);
- }
- this._board.render();
- }
- },
-
-
- // Theme
- _theme: {
-
- },
-
-
- // UI Elements
- _$game: null,
- _$canvas: null,
- _$gameholder: null,
- _$start: null,
- _$gameover: null,
- _$score: null,
- _$scoreText: null,
-
-
- // Canvas
- _canvas: null,
- _ctx: null,
-
-
- // Initialization
- _create: function() {
-
- var game = this;
-
- this.theme(this.options.theme);
-
- this._createHolder();
- this._createUI();
-
- this._refreshBlockSizes();
-
- this.updateSizes();
-
- $(window).resize(function(){
- //game.updateSizes();
- });
-
- this._SetupShapeFactory();
- this._SetupFilled();
- this._SetupInfo();
- this._SetupBoard();
-
- this._info.init();
- this._board.init();
-
- var renderLoop = function(){
- requestAnimationFrame(renderLoop);
- game._board.render();
- };
- renderLoop();
-
- if( this.options.autoplay ) {
- this.autoplay(true);
- this._setupTouchControls(false);
- } else {
- this._setupControls(true);
- this._setupTouchControls(false);
- }
-
- },
-
- _checkCollisions: function(x, y, blocks, checkDownOnly) {
- // x & y should be aspirational values
- var i = 0, len = blocks.length, a, b;
- for (; i= this._BLOCK_HEIGHT || this._filled.check(a, b)) {
- return true;
- } else if (!checkDownOnly && a < 0 || a >= this._BLOCK_WIDTH) {
- return true;
- }
- }
- return false;
- },
-
-
- _board: null,
- _info: null,
- _filled: null,
-
-
- /**
- * Draws the background
- */
- _drawBackground: function() {
-
- if( typeof this._theme.background !== 'string' ) {
- return;
- }
-
- if( this._theme.backgroundGrid instanceof Image ) {
-
- // Not loaded
- if( this._theme.backgroundGrid.width === 0 || this._theme.backgroundGrid.height === 0 ){ return; }
-
- this._ctx.globalAlpha = 1.0;
-
- for( var x=0; x maxx) { maxx = blocks[i]; }
- if (blocks[i+1] < miny) { miny = blocks[i+1]; }
- if (blocks[i+1] > maxy) { maxy = blocks[i+1]; }
- }
- return {
- left: minx,
- right: maxx,
- top: miny,
- bottom: maxy,
- width: maxx - minx,
- height: maxy - miny
- };
- }
- });
-
- return this.init();
- };
-
- this._shapeFactory = {
- line: function() {
- return new Shape(game, game._shapes.line, false, 'line');
- },
- square: function() {
- return new Shape(game, game._shapes.square, false, 'square');
- },
- arrow: function() {
- return new Shape(game, game._shapes.arrow, false, 'arrow');
- },
- leftHook: function() {
- return new Shape(game, game._shapes.leftHook, false, 'leftHook');
- },
- rightHook: function() {
- return new Shape(game, game._shapes.rightHook, false, 'rightHook');
- },
- leftZag: function() {
- return new Shape(game, game._shapes.leftZag, false, 'leftZag');
- },
- rightZag: function() {
- return new Shape(game, game._shapes.rightZag, false, 'rightZag');
- }
- };
- },
-
-
- _SetupFilled: function() {
- var game = this;
- if( this._filled !== null ){ return; }
-
- this._filled = {
- data: new Array(game._BLOCK_WIDTH * game._BLOCK_HEIGHT),
- score: 0,
- toClear: {},
- check: function(x, y) {
- return this.data[this.asIndex(x, y)];
- },
- add: function(x, y, blockType, blockVariation, blockIndex, blockOrientation) {
- if (x >= 0 && x < game._BLOCK_WIDTH && y >= 0 && y < game._BLOCK_HEIGHT) {
- this.data[this.asIndex(x, y)] = {
- blockType: blockType,
- blockVariation: blockVariation,
- blockIndex: blockIndex,
- blockOrientation: blockOrientation
- };
- }
- },
- getFreeSpaces: function() {
- var count = 0;
- for( var i=0; i=0; i--) {
- this.data[i] = (i >= game._BLOCK_WIDTH ? this.data[i-game._BLOCK_WIDTH] : undefined);
- }
- },
- checkForClears: function() {
- var startLines = game._board.lines;
- var rows = [], i, len, count, mod;
-
- for (i=0, len=this.data.length; i 1 ) {
- game._board.dropDelay *= 0.9;
- }
- }
-
- var clearedLines = game._board.lines - startLines;
- this._updateScore(clearedLines);
- },
- _updateScore: function(numLines) {
- if( numLines <= 0 ) { return; }
- var scores = [0,400,1000,3000,12000];
- if( numLines >= scores.length ){ numLines = scores.length-1 }
-
- this.score += scores[numLines];
- game._$scoreText.text(this.score);
-
- game.options.onLine.call(game.element, numLines, scores[numLines], this.score);
- },
- _resetScore: function() {
- this.score = 0;
- game._$scoreText.text(this.score);
- },
- draw: function() {
- for (var i=0, len=this.data.length, row, color; i= this.dropDelay) ||
- (game.options.autoplay) ||
- (this.holding.drop && (now - this.holding.drop) >= this.holdingThreshold) ) {
- drop = true;
- moved = true;
- this.dropCount = 0;
- }
-
- // Move Left by holding
- if( this.holding.left && (now - this.holding.left) >= this.holdingThreshold ) {
- moved = true;
- this.cur.moveLeft();
- }
-
- // Move Right by holding
- if( this.holding.right && (now - this.holding.right) >= this.holdingThreshold ) {
- moved = true;
- this.cur.moveRight();
- }
-
- // Test for a collision, add the piece to the filled blocks and fetch the next one
- if (drop) {
- var cur = this.cur, x = cur.x, y = cur.y, blocks = cur.getBlocks();
- if (game._checkCollisions(x, y+1, blocks, true)) {
- drop = false;
- var blockIndex = 0;
- for (var i=0; i 0) {
- return blockTheme[0];
- } else {
- return null;
- }
- } else {
- return blockTheme;
- }
- }
-
- if( typeof falling !== 'boolean' ){ falling = true; }
- if( falling ) {
- if( typeof game._theme.primary === 'string' && game._theme.primary !== '' ) {
- return game._theme.primary;
- } else if( typeof game._theme.blocks !== 'undefined' && game._theme.blocks !== null ) {
- return getBlockVariation(game._theme.blocks[blockType], blockVariation);
- } else {
- return getBlockVariation(game._theme.complexBlocks[blockType], blockVariation);
- }
- } else {
- if( typeof game._theme.secondary === 'string' && game._theme.secondary !== '' ) {
- return game._theme.secondary;
- } else if( typeof game._theme.blocks !== 'undefined' && game._theme.blocks !== null ) {
- return getBlockVariation(game._theme.blocks[blockType], blockVariation);
- } else {
- return getBlockVariation(game._theme.complexBlocks[blockType], blockVariation);
- }
- }
- }
-
- };
-
- game._niceShapes = game._getNiceShapes();
- },
-
- // Utility Functions
- _randInt: function(a, b) { return a + Math.floor(Math.random() * (1 + b - a)); },
- _randSign: function() { return this._randInt(0, 1) * 2 - 1; },
- _randChoice: function(choices) { return choices[this._randInt(0, choices.length-1)]; },
-
-
- /**
- * Find base64 encoded images and load them as image objects, which can be used by the canvas renderer
- */
- _preloadThemeAssets: function() {
-
- var game = this;
-
- var hexColorcheck = new RegExp('^#[A-F0-9+]{3,6}', 'i');
- var base64check = new RegExp('^data:image/(png|gif|jpg);base64,', 'i');
-
- var handleAssetLoad = function() {
- // Rerender the board as soon as an asset loads
- if( game._board ) {
- game._board.render(true);
- }
- };
-
- var loadAsset = function(src) {
- var plainSrc = src;
- if( ! hexColorcheck.test( plainSrc ) ) {
- // It's an image
- src = new Image();
- src.src = plainSrc;
- src.onload = handleAssetLoad;
- } else {
- // It's a color
- src = plainSrc;
- }
- return src;
- };
-
- var startAssetLoad = function(block) {
- // Assets can be an array of variation so they can change color/design randomly
- if( $.isArray(block) && block.length > 0 ) {
- for( var i=0; i
');
- this._$gameholder.css('position', 'relative').css('width', '100%').css('height', '100%');
-
- this.element.html('').append(this._$gameholder);
-
- // Create the game canvas and context
- this._$canvas = $('');
- if( typeof this._theme.background === 'string' ) {
- this._$canvas.css('background-color', this._theme.background);
- }
- this._$gameholder.append(this._$canvas);
-
- this._canvas = this._$canvas.get(0);
- this._ctx = this._canvas.getContext('2d');
-
- },
-
-
- _createUI: function() {
-
- var game = this;
-
- // Score
- game._$score = $(
- ''+
- '
'+
- '
'+ this.options.scoreText +'
'+
- '
0
'+
- '
'+
- '
').hide();
- game._$scoreText = game._$score.find('.blockrain-score-num');
- game._$gameholder.append(game._$score);
-
- // Create the start menu
- game._$start = $(
- '').hide();
- game._$gameholder.append(game._$start);
-
- game._$start.find('.blockrain-start-btn').click(function(event){
- event.preventDefault();
- game.start();
- });
-
- // Create the game over menu
- game._$gameover = $(
- '').hide();
- game._$gameover.find('.blockrain-game-over-btn').click(function(event){
- event.preventDefault();
- game.restart();
- });
- game._$gameholder.append(game._$gameover);
-
- this._createControls();
- },
-
-
- _createControls: function() {
-
- var game = this;
-
- game._$touchLeft = $('').appendTo(game._$gameholder);
- game._$touchRight = $('').appendTo(game._$gameholder);
- game._$touchRotateRight = $('').appendTo(game._$gameholder);
- game._$touchRotateLeft = $('').appendTo(game._$gameholder);
- game._$touchDrop = $('').appendTo(game._$gameholder);
-
- },
-
-
- _refreshBlockSizes: function() {
-
- if( this.options.autoBlockWidth ) {
- this.options.blockWidth = Math.ceil( this.element.width() / this.options.autoBlockSize );
- }
-
- },
-
-
- _getNiceShapes: function() {
- /*
- * Things I need for this to work...
- * - ability to test each shape with this._filled data
- * - maybe give empty spots scores? and try to maximize the score?
- */
-
- var game = this;
-
- var shapes = {},
- attr;
-
- for( var attr in this._shapeFactory ) {
- shapes[attr] = this._shapeFactory[attr]();
- }
-
- function scoreBlocks(possibles, blocks, x, y, filled, width, height) {
- var i, len=blocks.length, score=0, bottoms = {}, tx, ty, overlaps;
-
- // base score
- for (i=0; i best_score_for_shape) {
- best_score_for_shape = score;
- best_orientation_for_shape = i;
- best_x_for_shape = x;
- }
- break;
- }
- }
- }
- }
-
- if ((evil && best_score_for_shape < best_score) ||
- (!evil && best_score_for_shape > best_score)) {
- best_shape = shape;
- best_score = best_score_for_shape;
- best_orientation = best_orientation_for_shape;
- best_x = best_x_for_shape;
- }
- }
-
- best_shape.best_orientation = best_orientation;
- best_shape.best_x = best_x;
-
- return best_shape;
- };
-
- func.no_preview = true;
- return func;
- },
-
-
- _randomShapes: function() {
- // Todo: The shapefuncs should be cached.
- var shapeFuncs = [];
- $.each(this._shapeFactory, function(k,v) { shapeFuncs.push(v); });
-
- return this._randChoice(shapeFuncs);
- },
-
-
- /**
- * Controls
- */
- _setupControls: function(enable) {
-
- var game = this;
-
- var moveLeft = function(start) {
- if( ! start ) { game._board.holding.left = null; return; }
- if( ! game._board.holding.left ) {
- game._board.cur.moveLeft();
- game._board.holding.left = Date.now();
- game._board.holding.right = null;
- }
- }
- var moveRight = function(start) {
- if( ! start ) { game._board.holding.right = null; return; }
- if( ! game._board.holding.right ) {
- game._board.cur.moveRight();
- game._board.holding.right = Date.now();
- game._board.holding.left = null;
- }
- }
- var drop = function(start) {
- if( ! start ) { game._board.holding.drop = null; return; }
- if( ! game._board.holding.drop ) {
- game._board.cur.drop();
- game._board.holding.drop = Date.now();
- }
- }
- var rotateLeft = function() {
- game._board.cur.rotate('left');
- }
- var rotateRight = function() {
- game._board.cur.rotate('right');
- }
-
- // Handlers: These are used to be able to bind/unbind controls
- var handleKeyDown = function(evt) {
- if( ! game._board.cur ) { return true; }
- var caught = false;
-
- caught = true;
- if (game.options.asdwKeys) {
- switch(evt.keyCode) {
- case 65: /*a*/ moveLeft(true); break;
- case 68: /*d*/ moveRight(true); break;
- case 83: /*s*/ drop(true); break;
- case 87: /*w*/ game._board.cur.rotate('right'); break;
- }
- }
- switch(evt.keyCode) {
- case 37: /*left*/ moveLeft(true); break;
- case 39: /*right*/ moveRight(true); break;
- case 40: /*down*/ drop(true); break;
- case 38: /*up*/ game._board.cur.rotate('right'); break;
- case 88: /*x*/ game._board.cur.rotate('right'); break;
- case 90: /*z*/ game._board.cur.rotate('left'); break;
- default: caught = false;
- }
- if (caught) evt.preventDefault();
- return !caught;
- };
-
-
- var handleKeyUp = function(evt) {
- if( ! game._board.cur ) { return true; }
- var caught = false;
-
- caught = true;
- if (game.options.asdwKeys) {
- switch(evt.keyCode) {
- case 65: /*a*/ moveLeft(false); break;
- case 68: /*d*/ moveRight(false); break;
- case 83: /*s*/ drop(false); break;
- }
- }
- switch(evt.keyCode) {
- case 37: /*left*/ moveLeft(false); break;
- case 39: /*right*/ moveRight(false); break;
- case 40: /*down*/ drop(false); break;
- default: caught = false;
- }
- if (caught) evt.preventDefault();
- return !caught;
- };
-
- function isStopKey(evt) {
- var cfg = {
- stopKeys: {37:1, 38:1, 39:1, 40:1}
- };
-
- var isStop = (cfg.stopKeys[evt.keyCode] || (cfg.moreStopKeys && cfg.moreStopKeys[evt.keyCode]));
- if (isStop) evt.preventDefault();
- return isStop;
- }
-
- function getKey(evt) { return 'safekeypress.' + evt.keyCode; }
-
- function keydown(evt) {
- var key = getKey(evt);
- $.data(this, key, ($.data(this, key) || 0) - 1);
- return handleKeyDown.call(this, evt);
- }
-
- function keyup(evt) {
- $.data(this, getKey(evt), 0);
- handleKeyUp.call(this, evt);
- return isStopKey(evt);
- }
-
- // Unbind everything by default
- // Use event namespacing so we don't ruin other keypress events
- $(document) .unbind('keydown.blockrain')
- .unbind('keyup.blockrain');
-
- if( ! game.options.autoplay ) {
- if( enable ) {
- $(document) .bind('keydown.blockrain', keydown)
- .bind('keyup.blockrain', keyup);
- }
- }
- },
-
-
- _setupTouchControls: function(enable) {
-
- var game = this;
-
- // Movements can be held for faster movement
- var moveLeft = function(event){
- event.preventDefault();
- game._board.cur.moveLeft();
- game._board.holding.left = Date.now();
- game._board.holding.right = null;
- game._board.holding.drop = null;
- };
- var moveRight = function(event){
- event.preventDefault();
- game._board.cur.moveRight();
- game._board.holding.right = Date.now();
- game._board.holding.left = null;
- game._board.holding.drop = null;
- };
- var drop = function(event){
- event.preventDefault();
- game._board.cur.drop();
- game._board.holding.drop = Date.now();
- };
- var endMoveLeft = function(event){
- event.preventDefault();
- game._board.holding.left = null;
- };
- var endMoveRight = function(event){
- event.preventDefault();
- game._board.holding.right = null;
- };
- var endDrop = function(event){
- event.preventDefault();
- game._board.holding.drop = null;
- };
-
- // Rotations can't be held
- var rotateLeft = function(event){
- event.preventDefault();
- game._board.cur.rotate('left');
- };
- var rotateRight = function(event){
- event.preventDefault();
- game._board.cur.rotate('right');
- };
-
- // Unbind everything by default
- game._$touchLeft.unbind('touchstart touchend click');
- game._$touchRight.unbind('touchstart touchend click');
- game._$touchRotateLeft.unbind('touchstart touchend click');
- game._$touchRotateRight.unbind('touchstart touchend click');
- game._$touchDrop.unbind('touchstart touchend click');
-
- if( ! game.options.autoplay && enable ) {
- game._$touchLeft.show().bind('touchstart click', moveLeft).bind('touchend', endMoveLeft);
- game._$touchRight.show().bind('touchstart click', moveRight).bind('touchend', endMoveRight);
- game._$touchDrop.show().bind('touchstart click', drop).bind('touchend', endDrop);
- game._$touchRotateLeft.show().bind('touchstart click', rotateLeft);
- game._$touchRotateRight.show().bind('touchstart click', rotateRight);
- } else {
- game._$touchLeft.hide();
- game._$touchRight.hide();
- game._$touchRotateLeft.hide();
- game._$touchRotateRight.hide();
- game._$touchDrop.hide();
- }
-
- }
-
- });
-
-})(jQuery));
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..e6ff363
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,1443 @@
+import $ from 'jquery';
+
+import Shape from './Shape';
+import BlockrainThemes from './themes';
+
+
+
+const defaultOptions = {
+ autoplay: false, // Let a bot play the game
+ autoplayRestart: true, // Restart the game automatically once a bot loses
+ showFieldOnStart: true, // Show a bunch of random blocks on the start screen (it looks nice)
+ theme: null, // The theme name or a theme object
+ blockWidth: 10, // How many blocks wide the field is (The standard is 10 blocks)
+ autoBlockWidth: false, // The blockWidth is dinamically calculated based on the autoBlockSize. Disabled blockWidth. Useful for responsive backgrounds
+ autoBlockSize: 24, // The max size of a block for autowidth mode
+ difficulty: 'normal', // Difficulty (normal|nice|evil).
+ speed: 20, // The speed of the game. The higher, the faster the pieces go.
+ asdwKeys: true, // Enable ASDW keys
+
+ // Copy
+ playText: 'Let\'s play some Tetris',
+ playButtonText: 'Play',
+ gameOverText: 'Game Over',
+ restartButtonText: 'Play Again',
+ scoreText: 'Score',
+
+ // Basic Callbacks
+ onStart: function() {},
+ onRestart: function() {},
+ onGameOver: function(score) {},
+
+ // When a block is placed
+ onPlaced: function() {},
+ // When a line is made. Returns the number of lines, score assigned and total score
+ onLine: function(lines, scoreIncrement, score) {}
+}
+
+class Blockrain {
+
+ constructor($el, options) {
+ this.element = $el;
+ this.options = $.extend({}, defaultOptions, options);
+
+ // Theme
+ this._theme = {};
+
+ // UI Elements
+ this._$game = null;
+ this._$canvas = null;
+ this._$gameholder = null;
+ this._$start = null;
+ this._$gameover = null;
+ this._$score = null;
+ this._$scoreText = null;
+
+ // Canvas
+ this._canvas = null;
+ this._ctx = null;
+
+ this._board = null;
+ this._info = null;
+ this._filled = null;
+
+ /**
+ * Shapes
+ */
+ _shapeFactory = null;
+
+ /**
+ * The shapes have a reference point (the dot) and always rotate left.
+ * Keep in mind that the blocks should keep in the same relative position when rotating,
+ * to allow for custom per-block themes.
+ */
+ _shapes = {
+ /*
+ * X
+ * O XOXX
+ * X
+ * X
+ * . .
+ */
+ line: [
+ [ 0, -1, 0, -2, 0, -3, 0, -4],
+ [ 2, -2, 1, -2, 0, -2, -1, -2],
+ [ 0, -4, 0, -3, 0, -2, 0, -1],
+ [-1, -2, 0, -2, 1, -2, 2, -2]
+ ],
+ /*
+ * XX
+ * XX
+ */
+ square: [
+ [0, 0, 1, 0, 0, -1, 1, -1],
+ [1, 0, 1, -1, 0, 0, 0, -1],
+ [1, -1, 0, -1, 1, 0, 0, 0],
+ [0, -1, 0, 0, 1, -1, 1, 0]
+ ],
+ /*
+ * X X X
+ * XOX XO XOX OX
+ * . .X .X .X
+ */
+ arrow: [
+ [0, -1, 1, -1, 2, -1, 1, -2],
+ [1, 0, 1, -1, 1, -2, 0, -1],
+ [2, -1, 1, -1, 0, -1, 1, 0],
+ [1, -2, 1, -1, 1, 0, 2, -1]
+ ],
+ /*
+ * X X XX
+ * O XOX O XOX
+ * .XX . .X X
+ */
+ rightHook: [
+ [2, 0, 1, 0, 1, -1, 1, -2],
+ [2, -2, 2, -1, 1, -1, 0, -1],
+ [0, -2, 1, -2, 1, -1, 1, 0],
+ [0, 0, 0, -1, 1, -1, 2, -1]
+ ],
+ /*
+ * X XX X
+ * O XOX O XOX
+ * XX . X .X .
+ */
+ leftHook: [
+ [0, 0, 1, 0, 1, -1, 1, -2],
+ [2, 0, 2, -1, 1, -1, 0, -1],
+ [2, -2, 1, -2, 1, -1, 1, 0],
+ [0, -2, 0, -1, 1, -1, 2, -1]
+ ],
+ /*
+ * X XX
+ * XO OX
+ * X .
+ */
+ leftZag: [
+ [0, 0, 0, -1, 1, -1, 1, -2],
+ [2, -1, 1, -1, 1, -2, 0, -2],
+ [1, -2, 1, -1, 0, -1, 0, 0],
+ [0, -2, 1, -2, 1, -1, 2, -1]
+ ],
+ /*
+ * X
+ * XO OX
+ * .X XX
+ */
+ rightZag: [
+ [1, 0, 1, -1, 0, -1, 0, -2],
+ [2, -1, 1, -1, 1, 0, 0, 0],
+ [0, -2, 0, -1, 1, -1, 1, 0],
+ [0, 0, 1, 0, 1, -1, 2, -1]
+ ]
+ };
+
+ }
+
+ /**
+ * Start/Restart Game
+ */
+ start() {
+ this._doStart();
+ this.options.onStart.call(this.element);
+ }
+
+ restart() {
+ this._doStart();
+ this.options.onRestart.call(this.element);
+ }
+
+ gameover() {
+ this.showGameOverMessage();
+ this._board.gameover = true;
+ this.options.onGameOver.call(this.element, this._filled.score);
+ }
+
+ _doStart() {
+ this._filled.clearAll();
+ this._filled._resetScore();
+ this._board.cur = this._board.nextShape();
+ this._board.started = true;
+ this._board.gameover = false;
+ this._board.dropDelay = 5;
+ this._board.render(true);
+ this._board.animate();
+
+ this._$start.fadeOut(150);
+ this._$gameover.fadeOut(150);
+ this._$score.fadeIn(150);
+ }
+
+ pause() {
+ this._board.paused = true;
+ }
+
+ resume() {
+ this._board.paused = false;
+ }
+
+ autoplay(enable=true) {
+ // On autoplay, start the game right away
+ this.options.autoplay = enable;
+ if (enable && !this._board.started) {
+ this._doStart();
+ }
+ this._setupControls(!enable);
+ this._setupTouchControls(!enable);
+ }
+
+ controls(enable=true) {
+ this._setupControls(enable);
+ }
+
+ touchControls(enable=true) {
+ this._setupTouchControls(enable);
+ }
+
+ score(newScore) {
+ if (typeof newScore !== 'undefined' && parseInt(newScore) >= 0) {
+ this._filled.score = parseInt(newScore);
+ this._$scoreText.text(this._filled_score);
+ }
+ return this._filled.score;
+ }
+
+ freesquares() {
+ return this._filled.getFreeSpaces();
+ }
+
+ showStartMessage() {
+ this._$start.show();
+ }
+
+ showGameOverMessage() {
+ this._$gameover.show();
+ }
+
+ /**
+ * Update the sizes of the renderer (this makes the game responsive)
+ */
+ updateSizes() {
+ this._PIXEL_WIDTH = this.element.innerWidth();
+ this._PIXEL_HEIGHT = this.element.innerHeight();
+
+ this._BLOCK_WIDTH = this.options.blockWidth;
+ this._BLOCK_HEIGHT = Math.floor(this.element.innerHeight() / this.element.innerWidth() * this._BLOCK_WIDTH);
+
+ this._block_size = Math.floor(this._PIXEL_WIDTH / this._BLOCK_WIDTH);
+ this._border_width = 2;
+
+ // Recalculate the pixel width and height so the canvas always has the best possible size
+ this._PIXEL_WIDTH = this._block_size * this._BLOCK_WIDTH;
+ this._PIXEL_HEIGHT = this._block_size * this._BLOCK_HEIGHT;
+
+ this._$canvas .attr('width', this._PIXEL_WIDTH)
+ .attr('height', this._PIXEL_HEIGHT);
+ }
+
+ theme(newTheme) {
+ if (typeof newTheme === 'undefined') {
+ return this.options.theme || this._theme;
+ }
+
+ // Setup the theme properly
+ if (typeof newTheme === 'string') {
+ this.options.theme = newTheme;
+ this._theme = $.extend(true, {}, BlockrainThemes[newTheme]);
+ }
+ else {
+ this.options.theme = null;
+ this._theme = newTheme;
+ }
+
+ if (typeof this._theme === 'undefined' || this._theme === null) {
+ this._theme = $.extend(true, {}, BlockrainThemes['retro']);
+ this.options.theme = 'retro';
+ }
+
+ if (isNaN(parseInt(this._theme.strokeWidth)) || typeof parseInt(this._theme.strokeWidth) !== 'number') {
+ this._theme.strokeWidth = 2;
+ }
+
+ // Load the image assets
+ this._preloadThemeAssets();
+
+ if (this._board !== null) {
+ if (typeof this._theme.background === 'string') {
+ this._$canvas.css('background-color', this._theme.background);
+ }
+ this._board.render();
+ }
+ }
+
+ // Initialization
+ _create() {
+ let game = this;
+
+ this.theme(this.options.theme);
+
+ this._createHolder();
+ this._createUI();
+
+ this._refreshBlockSizes();
+
+ this.updateSizes();
+
+ // $(window).resize(() => this.updateSizes);
+
+ this._SetupShapeFactory();
+ this._SetupFilled();
+ this._SetupInfo();
+ this._SetupBoard();
+
+ this._info.init();
+ this._board.init();
+
+ const renderLoop = function() {
+ requestAnimationFrame(renderLoop);
+ game._board.render();
+ };
+ renderLoop();
+
+ if (this.options.autoplay) {
+ this.autoplay(true);
+ this._setupTouchControls(false);
+ } else {
+ this._setupControls(true);
+ this._setupTouchControls(false);
+ }
+ }
+
+ _checkCollisions(x, y, blocks, checkDownOnly) {
+ // x & y should be aspirational values
+ let i = 0, len = blocks.length, a, b;
+ for (; i= this._BLOCK_HEIGHT || this._filled.check(a, b)) {
+ return true;
+ } else if (!checkDownOnly && a < 0 || a >= this._BLOCK_WIDTH) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Draws the background
+ */
+ _drawBackground() {
+ if (typeof this._theme.background !== 'string') {
+ return;
+ }
+
+ if (this._theme.backgroundGrid instanceof Image) {
+
+ // Not loaded
+ if (this._theme.backgroundGrid.width === 0 || this._theme.backgroundGrid.height === 0) {
+ return;
+ }
+
+ this._ctx.globalAlpha = 1.0;
+
+ for (let x = 0; x < this._BLOCK_WIDTH; x++) {
+ for (let y = 0; y < this._BLOCK_HEIGHT; y++) {
+ let cx = x * this._block_size;
+ let cy = y * this._block_size;
+
+ this._ctx.drawImage(this._theme.backgroundGrid,
+ 0, 0, this._theme.backgroundGrid.width, this._theme.backgroundGrid.height,
+ cx, cy, this._block_size, this._block_size);
+ }
+ }
+
+ } else if (typeof this._theme.backgroundGrid === 'string') {
+
+ let borderWidth = this._theme.strokeWidth;
+ let borderDistance = Math.round(this._block_size * 0.23);
+ let squareDistance = Math.round(this._block_size * 0.30);
+
+ this._ctx.globalAlpha = 1.0;
+ this._ctx.fillStyle = this._theme.backgroundGrid;
+
+ for (let x = 0; x < this._BLOCK_WIDTH; x++) {
+ for (let y = 0; y < this._BLOCK_HEIGHT; y++) {
+ let cx = x * this._block_size;
+ let cy = y * this._block_size;
+
+ this._ctx.fillRect(cx+borderWidth, cy+borderWidth, this._block_size-borderWidth * 2, this._block_size-borderWidth * 2);
+ }
+ }
+
+ }
+
+ this._ctx.globalAlpha = 1.0;
+ }
+
+ // TODO: move setups into constructor?
+
+ _SetupShapeFactory() {
+ let game = this;
+ if (this._shapeFactory !== null) { return; }
+
+ this._shapeFactory = {
+ line: function() {
+ return new Shape(game, game._shapes.line, false, 'line');
+ },
+ square: function() {
+ return new Shape(game, game._shapes.square, false, 'square');
+ },
+ arrow: function() {
+ return new Shape(game, game._shapes.arrow, false, 'arrow');
+ },
+ leftHook: function() {
+ return new Shape(game, game._shapes.leftHook, false, 'leftHook');
+ },
+ rightHook: function() {
+ return new Shape(game, game._shapes.rightHook, false, 'rightHook');
+ },
+ leftZag: function() {
+ return new Shape(game, game._shapes.leftZag, false, 'leftZag');
+ },
+ rightZag: function() {
+ return new Shape(game, game._shapes.rightZag, false, 'rightZag');
+ }
+ };
+ }
+
+ _SetupFilled() {
+ let game = this;
+ if (this._filled !== null) { return; }
+
+ this._filled = {
+ data: new Array(game._BLOCK_WIDTH * game._BLOCK_HEIGHT),
+ score: 0,
+ toClear: {},
+ check: function(x, y) {
+ return this.data[this.asIndex(x, y)];
+ },
+ add: function(x, y, blockType, blockVariation, blockIndex, blockOrientation) {
+ if (x >= 0 && x < game._BLOCK_WIDTH && y >= 0 && y < game._BLOCK_HEIGHT) {
+ this.data[this.asIndex(x, y)] = {
+ blockType: blockType,
+ blockVariation: blockVariation,
+ blockIndex: blockIndex,
+ blockOrientation: blockOrientation
+ };
+ }
+ },
+ getFreeSpaces: function() {
+ let count = 0;
+ for (let i = 0; i < this.data.length; i++) {
+ count += (this.data[i] ? 1 : 0);
+ }
+ },
+ asIndex: function(x, y) {
+ return x + y * game._BLOCK_WIDTH;
+ },
+ asX: function(index) {
+ return index % game._BLOCK_WIDTH;
+ },
+ asY: function(index) {
+ return Math.floor(index / game._BLOCK_WIDTH);
+ },
+ clearAll: function() {
+ this.data = new Array(game._BLOCK_WIDTH * game._BLOCK_HEIGHT);
+ },
+ _popRow: function(row_to_pop) {
+ for (let i = game._BLOCK_WIDTH * (row_to_pop + 1) - 1; i >= 0; i--) {
+ this.data[i] = i >= game._BLOCK_WIDTH ? this.data[i - game._BLOCK_WIDTH] : void 0;
+ }
+ },
+ checkForClears: function() {
+ let startLines = game._board.lines;
+ let rows = [], i, len, count, mod;
+
+ for (let i = 0, len = this.data.length; i < len; i++) {
+ mod = this.asX(i);
+ if (mod == 0) count = 0;
+ if (this.data[i] && typeof this.data[i] !== 'undefined' && typeof this.data[i].blockType === 'string') {
+ count += 1;
+ }
+ if (mod == game._BLOCK_WIDTH - 1 && count == game._BLOCK_WIDTH) {
+ rows.push(this.asY(i));
+ }
+ }
+
+ for (let i = 0, len = rows.length; i < len; i++) {
+ this._popRow(rows[i]);
+ game._board.lines++;
+ if (game._board.lines % 10 == 0 && game._board.dropDelay > 1) {
+ game._board.dropDelay *= 0.9;
+ }
+ }
+
+ let clearedLines = game._board.lines - startLines;
+ this._updateScore(clearedLines);
+ },
+ _updateScore: function(numLines) {
+ if (numLines <= 0) { return; }
+ let scores = [0, 400, 1000, 3000, 12000];
+ if (numLines >= scores.length) {
+ numLines = scores.length - 1;
+ }
+
+ this.score += scores[numLines];
+ game._$scoreText.text(this.score);
+
+ game.options.onLine.call(game.element, numLines, scores[numLines], this.score);
+ },
+ _resetScore: function() {
+ this.score = 0;
+ game._$scoreText.text(this.score);
+ },
+ draw: function() {
+ for (let i = 0, len = this.data.length, row, color; i= this.dropDelay) ||
+ (game.options.autoplay) ||
+ (this.holding.drop && (now - this.holding.drop) >= this.holdingThreshold)) {
+ drop = true;
+ moved = true;
+ this.dropCount = 0;
+ }
+
+ // Move Left by holding
+ if (this.holding.left && (now - this.holding.left) >= this.holdingThreshold) {
+ moved = true;
+ this.cur.moveLeft();
+ }
+
+ // Move Right by holding
+ if (this.holding.right && (now - this.holding.right) >= this.holdingThreshold) {
+ moved = true;
+ this.cur.moveRight();
+ }
+
+ // Test for a collision, add the piece to the filled blocks and fetch the next one
+ if (drop) {
+ let cur = this.cur, x = cur.x, y = cur.y, blocks = cur.getBlocks();
+ if (game._checkCollisions(x, y+1, blocks, true)) {
+ drop = false;
+ let blockIndex = 0;
+ for (let i = 0; i < cur.blocksLen; i += 2) {
+ game._filled.add(x + blocks[i], y + blocks[i+1], cur.blockType, cur.blockVariation, blockIndex, cur.orientation);
+ if (y + blocks[i] < 0) {
+ gameOver = true;
+ }
+ blockIndex++;
+ }
+ game._filled.checkForClears();
+ this.cur = this.nextShape();
+ this.renderChanged = true;
+
+ // Stop holding drop (and any other buttons). Just in case the controls get sticky.
+ this.holding.left = null;
+ this.holding.right = null;
+ this.holding.drop = null;
+
+ game.options.onPlaced.call(game.element);
+ }
+ }
+ }
+
+ // Drop
+ if (drop) {
+ moved = true;
+ this.cur.y++;
+ }
+
+ if (drop || moved) {
+ this.renderChanged = true;
+ }
+
+ if (gameOver) {
+ this.gameover = true;
+
+ game.gameover();
+
+ if (game.options.autoplay && game.options.autoplayRestart) {
+ // On autoplay, restart the game automatically
+ game.restart();
+ }
+ this.renderChanged = true;
+
+ } else {
+
+ // Update the speed
+ this.animateDelay = 1000 / game.options.speed;
+
+ this.animateTimeoutId = window.setTimeout(function() {
+ game._board.animate();
+ }, this.animateDelay);
+
+ }
+ },
+
+ createRandomBoard: function() {
+ let start = [], blockTypes = [], i, ilen, j, jlen, blockType;
+
+ // Draw a random blockrain screen
+ blockTypes = Object.keys(game._shapeFactory);
+
+ for (let i=0, ilen=game._BLOCK_WIDTH; i 0) {
+ return blockTheme[0];
+ } else {
+ return null;
+ }
+ } else {
+ return blockTheme;
+ }
+ }
+
+ if (typeof falling !== 'boolean') { falling = true; }
+ if (falling) {
+ if (typeof game._theme.primary === 'string' && game._theme.primary !== '') {
+ return game._theme.primary;
+ } else if (typeof game._theme.blocks !== 'undefined' && game._theme.blocks !== null) {
+ return getBlockVariation(game._theme.blocks[blockType], blockVariation);
+ } else {
+ return getBlockVariation(game._theme.complexBlocks[blockType], blockVariation);
+ }
+ } else {
+ if (typeof game._theme.secondary === 'string' && game._theme.secondary !== '') {
+ return game._theme.secondary;
+ } else if (typeof game._theme.blocks !== 'undefined' && game._theme.blocks !== null) {
+ return getBlockVariation(game._theme.blocks[blockType], blockVariation);
+ } else {
+ return getBlockVariation(game._theme.complexBlocks[blockType], blockVariation);
+ }
+ }
+ }
+
+ };
+
+ game._niceShapes = game._getNiceShapes();
+ }
+
+ // Utility Functions
+ _randInt(a, b) {
+ return a + Math.floor(Math.random() * (1 + b - a));
+ }
+
+ _randSign() {
+ return this._randInt(0, 1) * 2 - 1;
+ }
+
+ _randChoice(choices) {
+ return choices[this._randInt(0, choices.length-1)];
+ }
+
+ /**
+ * Find base64 encoded images and load them as image objects, which can be used by the canvas renderer
+ */
+ _preloadThemeAssets() {
+ let game = this;
+ let hexColorcheck = new RegExp('^#[A-F0-9+]{3,6}', 'i');
+ let base64check = new RegExp('^data:image/(png|gif|jpg);base64,', 'i');
+
+ const handleAssetLoad = function() {
+ // Rerender the board as soon as an asset loads
+ if (game._board) {
+ game._board.render(true);
+ }
+ };
+
+ const loadAsset = function(src) {
+ let plainSrc = src;
+ if (!hexColorcheck.test(plainSrc)) {
+ // It's an image
+ src = new Image();
+ src.src = plainSrc;
+ src.onload = handleAssetLoad;
+ } else {
+ // It's a color
+ src = plainSrc;
+ }
+ return src;
+ };
+
+ const startAssetLoad = function(block) {
+ // Assets can be an array of variation so they can change color/design randomly
+ if (Array.isArray(block) && block.length > 0) {
+ for (let i = 0; i < block.length; i++) {
+ block[i] = loadAsset(block[i]);
+ }
+ } else if (typeof block === 'string') {
+ block = loadAsset(block);
+ }
+ return block;
+ };
+
+ // TODO: use same loop instead of 2. DRY
+ if (typeof this._theme.complexBlocks !== 'undefined') {
+ let keys = Object.keys(this._theme.complexBlocks);
+ // Load the complexBlocks
+ for (let i = 0; i < keys.length; i++) {
+ this._theme.complexBlocks[keys[i]] = startAssetLoad(this._theme.complexBlocks[keys[i]]);
+ }
+ } else if (typeof this._theme.blocks !== 'undefined') {
+ let keys = Object.keys(this._theme.blocks);
+ // Load the blocks
+ for (let i = 0; i < keys.length; i++) {
+ this._theme.blocks[keys[i]] = startAssetLoad(this._theme.blocks[keys[i]]);
+ }
+ }
+
+ // Load the bg
+ if (typeof this._theme.backgroundGrid !== 'undefined' &&
+ typeof this._theme.backgroundGrid === 'string' &&
+ !hexColorcheck.test(this._theme.backgroundGrid)) {
+ let src = this._theme.backgroundGrid;
+ this._theme.backgroundGrid = new Image();
+ this._theme.backgroundGrid.src = src;
+ this._theme.backgroundGrid.onload = handleAssetLoad;
+ }
+ }
+
+ _createHolder() {
+ // Create the main holder (it holds all the ui elements, the original element is just the wrapper)
+ this._$gameholder = $('');
+ this._$gameholder.css('position', 'relative').css('width', '100%').css('height', '100%');
+
+ this.element.html('').append(this._$gameholder);
+
+ // Create the game canvas and context
+ this._$canvas = $('');
+ if (typeof this._theme.background === 'string') {
+ this._$canvas.css('background-color', this._theme.background);
+ }
+ this._$gameholder.append(this._$canvas);
+
+ this._canvas = this._$canvas.get(0);
+ this._ctx = this._canvas.getContext('2d');
+ }
+
+ _createUI() {
+ // Score
+ this._$score = $(
+ ''+
+ '
'+
+ '
'+ this.options.scoreText +'
'+
+ '
0
'+
+ '
'+
+ '
').hide();
+ this._$scoreText = this._$score.find('.blockrain-score-num');
+ this._$gameholder.append(this._$score);
+
+ // Create the start menu
+ this._$start = $(
+ '').hide();
+ this._$gameholder.append(this._$start);
+
+ this._$start.find('.blockrain-start-btn').click((e) => {
+ e.preventDefault();
+ this.start();
+ });
+
+ // Create the game over menu
+ this._$gameover = $(
+ '').hide();
+ this._$gameover.find('.blockrain-game-over-btn').click((e) => {
+ e.preventDefault();
+ this.restart();
+ });
+ this._$gameholder.append(this._$gameover);
+
+ this._createControls();
+ }
+
+ _createControls() {
+ this._$touchLeft = $('').appendTo(this._$thisholder);
+ this._$touchRight = $('').appendTo(this._$thisholder);
+ this._$touchRotateRight = $('').appendTo(this._$thisholder);
+ this._$touchRotateLeft = $('').appendTo(this._$thisholder);
+ this._$touchDrop = $('').appendTo(this._$gameholder);
+ }
+
+ _refreshBlockSizes() {
+ if (this.options.autoBlockWidth) {
+ this.options.blockWidth = Math.ceil(this.element.width() / this.options.autoBlockSize);
+ }
+ }
+
+ /*
+ * Things I need for this to work...
+ * - ability to test each shape with this._filled data
+ * - maybe give empty spots scores? and try to maximize the score?
+ */
+ _getNiceShapes() {
+ let game = this;
+
+ let shapes = {},
+ attr;
+
+ for (let attr in this._shapeFactory) {
+ shapes[attr] = this._shapeFactory[attr]();
+ }
+
+ const scoreBlocks = function scoreBlocks(possibles, blocks, x, y, filled, width, height) {
+ let i, len=blocks.length, score=0, bottoms = {}, tx, ty, overlaps;
+
+ // base score
+ for (let i = 0; i < len; i += 2) {
+ score += possibles[game._filled.asIndex(x + blocks[i], y + blocks[i+1])] || 0;
+ }
+
+ // overlap score -- //TODO - don't count overlaps if cleared?
+ for (let i = 0; i < len; i += 2) {
+ tx = blocks[i];
+ ty = blocks[i+1];
+ if (typeof bottoms[tx] === 'undefined' || bottoms[tx] < ty) {
+ bottoms[tx] = ty;
+ }
+ }
+ overlaps = 0;
+ for (let tx in bottoms) {
+ tx = parseInt(tx);
+ for (let ty = bottoms[tx]+1, i = 0; y + ty < height; ty++, i++) {
+ if (!game._filled.check(x + tx, y + ty)) {
+ overlaps += i == 0 ? 2 : 1; //TODO-score better
+ //if (i == 0) overlaps += 1;
+ break;
+ }
+ }
+ }
+
+ score = score - overlaps;
+
+ return score;
+ }
+
+ const resetShapes = function resetShapes() {
+ for (let attr in shapes) {
+ shapes[attr].x = 0;
+ shapes[attr].y = -1;
+ }
+ }
+
+ //TODO -- evil mode needs to realize that overlap is bad...
+ const niceShapes = function niceShapes(filled, checkCollisions, width, height, mode, _one_shape) {
+ if (!_one_shape) resetShapes();
+
+ let possibles = new Array(width * height),
+ evil = mode == 'evil',
+ x, y, py,
+ attr, shape, i, blocks, bounds,
+ score, best_shape, best_score = (evil ? 1 : -1) * 999, best_orientation, best_x,
+ best_score_for_shape, best_orientation_for_shape, best_x_for_shape;
+
+ for (let x = 0; x < width; x++) {
+ for (let y = 0; y <= height; y++) {
+ if (y == height || filled.check(x, y)) {
+ for (let py = y - 4; py < y; py++) {
+ possibles[filled.asIndex(x, py)] = py; //TODO - figure out better scoring?
+ }
+ break;
+ }
+ }
+ }
+
+ // for each shape...
+ let opts = typeof _one_shape === 'undefined' ? shapes : {cur: _one_shape}; //BOO
+ for (let attr in opts) { //TODO - check in random order to prevent later shapes from winning
+ shape = opts[attr];
+ best_score_for_shape = -999;
+
+ // for each orientation...
+ for (let i = 0; i < (shape.symmetrical ? 2 : 4); i++) { //TODO - only look at unique orientations
+ blocks = shape.getBlocks(i);
+ bounds = shape.getBounds(blocks);
+
+ // try each possible position...
+ for (let x = -bounds.left; x < width - bounds.width; x++) {
+ for (let y = -1; y < height - bounds.bottom; y++) {
+ if (game._checkCollisions(x, y + 1, blocks, true)) {
+ // collision
+ score = scoreBlocks(possibles, blocks, x, y, filled, width, height);
+ if (score > best_score_for_shape) {
+ best_score_for_shape = score;
+ best_orientation_for_shape = i;
+ best_x_for_shape = x;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if ((evil && best_score_for_shape < best_score) ||
+ (!evil && best_score_for_shape > best_score)) {
+ best_shape = shape;
+ best_score = best_score_for_shape;
+ best_orientation = best_orientation_for_shape;
+ best_x = best_x_for_shape;
+ }
+ }
+
+ best_shape.best_orientation = best_orientation;
+ best_shape.best_x = best_x;
+
+ return best_shape;
+ };
+
+ niceShapes.no_preview = true;
+ return niceShapes;
+ }
+
+ _randomShapes() {
+ // Todo: The shapefuncs should be cached.
+ const shapeFuncs = Object
+ .keys(this._shapeFactory)
+ .map((k) => { return this._shapeFactory[k]})
+ ;
+
+ return this._randChoice(shapeFuncs);
+ }
+
+ /**
+ * Controls
+ * TODO: DRY this up!!!
+ */
+ _setupControls(enable) {
+ let game = this;
+
+ const moveLeft = function(start) {
+ if (!start) {
+ game._board.holding.left = null;
+ return;
+ }
+ if (!game._board.holding.left) {
+ game._board.cur.moveLeft();
+ game._board.holding.left = Date.now();
+ game._board.holding.right = null;
+ }
+ }
+ const moveRight = function(start) {
+ if (!start) {
+ game._board.holding.right = null;
+ return;
+ }
+ if (!game._board.holding.right) {
+ game._board.cur.moveRight();
+ game._board.holding.right = Date.now();
+ game._board.holding.left = null;
+ }
+ }
+ const drop = function(start) {
+ if (!start) {
+ game._board.holding.drop = null;
+ return;
+ }
+ if (!game._board.holding.drop) {
+ game._board.cur.drop();
+ game._board.holding.drop = Date.now();
+ }
+ }
+ const rotateLeft = function() {
+ game._board.cur.rotate('left');
+ }
+ const rotateRight = function() {
+ game._board.cur.rotate('right');
+ }
+
+ // Handlers: These are used to be able to bind/unbind controls
+ const handleKeyDown = function(evt) {
+ if (!game._board.cur) { return true; }
+ let caught = true;
+
+ if (game.options.asdwKeys) {
+ switch(evt.keyCode) {
+ case 65: /*a*/ moveLeft(true); break;
+ case 68: /*d*/ moveRight(true); break;
+ case 83: /*s*/ drop(true); break;
+ case 87: /*w*/ rotateRight(); break;
+ }
+ }
+ switch(evt.keyCode) {
+ case 37: /*left*/ moveLeft(true); break;
+ case 39: /*right*/ moveRight(true); break;
+ case 40: /*down*/ drop(true); break;
+ case 38: /*up*/ rotateRight(); break;
+ case 88: /*x*/ rotateRight(); break;
+ case 90: /*z*/ rotateLeft(); break;
+ default: caught = false;
+ }
+ if (caught) evt.preventDefault();
+ return !caught;
+ };
+
+ const handleKeyUp = function(evt) {
+ if (!game._board.cur) { return true; }
+ let caught = true;
+
+ if (game.options.asdwKeys) {
+ switch(evt.keyCode) {
+ case 65: /*a*/ moveLeft(false); break;
+ case 68: /*d*/ moveRight(false); break;
+ case 83: /*s*/ drop(false); break;
+ }
+ }
+ switch(evt.keyCode) {
+ case 37: /*left*/ moveLeft(false); break;
+ case 39: /*right*/ moveRight(false); break;
+ case 40: /*down*/ drop(false); break;
+ default: caught = false;
+ }
+ if (caught) evt.preventDefault();
+ return !caught;
+ };
+
+ const isStopKey = function(evt) {
+ let cfg = {
+ stopKeys: {37:1, 38:1, 39:1, 40:1}
+ };
+
+ let isStop = (cfg.stopKeys[evt.keyCode] || (cfg.moreStopKeys && cfg.moreStopKeys[evt.keyCode]));
+ if (isStop) evt.preventDefault();
+ return isStop;
+ }
+
+ const getKey = function(evt) { return 'safekeypress.' + evt.keyCode; }
+
+ const keydown = function(evt) {
+ let key = getKey(evt);
+ // TODO: before it was $.data(this) ... check this was the $el
+ $.data(game.element, key, ($.data(game.element, key) || 0) - 1);
+ return handleKeyDown(evt);
+ }
+
+ const keyup = function(evt) {
+ $.data(this, getKey(evt), 0);
+ handleKeyUp.call(this, evt);
+ return isStopKey(evt);
+ }
+
+ // Unbind everything by default
+ // Use event namespacing so we don't ruin other keypress events
+ $(document).unbind('keydown.blockrain')
+ .unbind('keyup.blockrain');
+
+ if (!game.options.autoplay && enable) {
+ $(document).bind('keydown.blockrain', keydown)
+ .bind('keyup.blockrain', keyup);
+ }
+ }
+
+ _setupTouchControls(enable) {
+ let game = this;
+
+ // Movements can be held for faster movement
+ const moveLeft = function(event) {
+ event.preventDefault();
+ game._board.cur.moveLeft();
+ game._board.holding.left = Date.now();
+ game._board.holding.right = null;
+ game._board.holding.drop = null;
+ };
+ const moveRight = function(event) {
+ event.preventDefault();
+ game._board.cur.moveRight();
+ game._board.holding.right = Date.now();
+ game._board.holding.left = null;
+ game._board.holding.drop = null;
+ };
+ const drop = function(event) {
+ event.preventDefault();
+ game._board.cur.drop();
+ game._board.holding.drop = Date.now();
+ };
+ const endMoveLeft = function(event) {
+ event.preventDefault();
+ game._board.holding.left = null;
+ };
+ const endMoveRight = function(event) {
+ event.preventDefault();
+ game._board.holding.right = null;
+ };
+ const endDrop = function(event) {
+ event.preventDefault();
+ game._board.holding.drop = null;
+ };
+
+ // Rotations can't be held
+ const rotateLeft = function(event) {
+ event.preventDefault();
+ game._board.cur.rotate('left');
+ };
+ const rotateRight = function(event) {
+ event.preventDefault();
+ game._board.cur.rotate('right');
+ };
+
+ // Unbind everything by default
+ game._$touchLeft.unbind('touchstart touchend click');
+ game._$touchRight.unbind('touchstart touchend click');
+ game._$touchRotateLeft.unbind('touchstart touchend click');
+ game._$touchRotateRight.unbind('touchstart touchend click');
+ game._$touchDrop.unbind('touchstart touchend click');
+
+ if (!game.options.autoplay && enable) {
+ game._$touchLeft.show().bind('touchstart click', moveLeft).bind('touchend', endMoveLeft);
+ game._$touchRight.show().bind('touchstart click', moveRight).bind('touchend', endMoveRight);
+ game._$touchDrop.show().bind('touchstart click', drop).bind('touchend', endDrop);
+ game._$touchRotateLeft.show().bind('touchstart click', rotateLeft);
+ game._$touchRotateRight.show().bind('touchstart click', rotateRight);
+ } else {
+ game._$touchLeft.hide();
+ game._$touchRight.hide();
+ game._$touchRotateLeft.hide();
+ game._$touchRotateRight.hide();
+ game._$touchDrop.hide();
+ }
+ }
+
+}
diff --git a/src/shape.js b/src/shape.js
new file mode 100644
index 0000000..ab34f78
--- /dev/null
+++ b/src/shape.js
@@ -0,0 +1,95 @@
+
+export default class Shape {
+
+ constructor(game, orientations, symmetrical, blockType) {
+ this.x = 0;
+ this.y = 0;
+ // TODO: remove dep to game, current dependency is the wrong way
+ this.game = game;
+ this.symmetrical = symmetrical;
+ this.blockType = blockType;
+ this.blockVariation = null;
+ this.blocksLen = orientations[0].length;
+ this.orientations = orientations;
+ this.orientation = 0; // 4 possible
+ }
+
+ init() {
+ this.orientation = 0;
+ this.x = Math.floor(this.game._BLOCK_WIDTH / 2) - 1;
+ this.y = -1;
+
+ return this;
+ }
+
+ rotate(direction) {
+ let orientation = (this.orientation + (direction === 'left' ? 1 : -1) + 4) % 4;
+
+ //TODO - when past limit - auto shift and remember that too!
+ if (!this.game._checkCollisions(this.x, this.y, this.getBlocks(orientation))) {
+ this.orientation = orientation;
+ this.game._board.renderChanged = true;
+ }
+ }
+
+ moveRight() {
+ if (!this.game._checkCollisions(this.x + 1, this.y, this.getBlocks())) {
+ this.x++;
+ this.game._board.renderChanged = true;
+ }
+ }
+
+ moveLeft() {
+ if (!this.game._checkCollisions(this.x - 1, this.y, this.getBlocks())) {
+ this.x--;
+ this.game._board.renderChanged = true;
+ }
+ }
+
+ drop() {
+ if (!this.game._checkCollisions(this.x, this.y + 1, this.getBlocks())) {
+ this.y++;
+ // Reset the drop count, as we dropped the block sooner
+ this.game._board.dropCount = -1;
+ this.game._board.animate();
+ this.game._board.renderChanged = true;
+ }
+ }
+
+ getBlocks(orientation) { // optional param
+ return this.orientations[typeof orientation !== 'undefined' ? orientation : this.orientation];
+ }
+
+ draw(_x, _y, _orientation) {
+ let blocks = this.getBlocks(_orientation),
+ x = typeof _x === 'undefined' ? this.x : _x,
+ y = typeof _y === 'undefined' ? this.y : _y,
+ i = 0,
+ index = 0;
+
+ for (; i < this.blocksLen; i += 2) {
+ this.game._board.drawBlock(x + blocks[i], y + blocks[i+1], this.blockType, this.blockletiation, index, this.orientation, true);
+ index++;
+ }
+ }
+
+ getBounds(_blocks) { // _blocks can be an array of blocks, an orientation index, or undefined
+ let blocks = Array.isArray(_blocks) ? _blocks : this.getBlocks(_blocks),
+ i=0, len=blocks.length, minx=999, maxx=-999, miny=999, maxy=-999;
+ for (; i < len; i += 2) {
+ if (blocks[i] < minx) { minx = blocks[i]; }
+ if (blocks[i] > maxx) { maxx = blocks[i]; }
+ if (blocks[i+1] < miny) { miny = blocks[i+1]; }
+ if (blocks[i+1] > maxy) { maxy = blocks[i+1]; }
+ }
+ return {
+ left: minx,
+ right: maxx,
+ top: miny,
+ bottom: maxy,
+ width: maxx - minx,
+ height: maxy - miny
+ };
+ }
+
+}
diff --git a/src/blockrain.jquery.themes.js b/src/themes.js
similarity index 99%
rename from src/blockrain.jquery.themes.js
rename to src/themes.js
index 9124c37..c92d425 100644
--- a/src/blockrain.jquery.themes.js
+++ b/src/themes.js
@@ -1,8 +1,8 @@
/**
* Themes. You can add more custom themes to this object.
*/
-window.BlockrainThemes = {
- 'custom': {
+export let BlockrainThemes = {
+ custom: {
background: '#040304',
backgroundGrid: 'data:image/png;base64,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',
complexBlocks: {
@@ -15,7 +15,7 @@ window.BlockrainThemes = {
leftZag: 'assets/blocks/custom/leftZag.png'
}
},
- 'candy': {
+ candy: {
background: '#040304',
backgroundGrid: 'data:image/png;base64,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',
blocks: {
@@ -28,7 +28,7 @@ window.BlockrainThemes = {
leftZag: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAIAAADZrBkAAAACtElEQVR4Xg2Lu25cZRhF93f5L3OOPeOQmJuDhEIHDa/CC/B4SHSICqS0CERDE2MJAYKEmITEcewZz/nvH7O0taq16eTL++P66nhCDegOAEhghFYhDDM0hSgowQkKkDIwJi0pP/72G9j24vIi8S7nN34lYG1VyOKg0uVKFFIiY/XL+R8s977+6nvttBMkovTDz9/Np3G3fTlvYkoFJswzaxtya2haQ0nUdGa2PLY6GD/9+vjJ+Y+bD+K2vFxtrLS3cQq9t1rLwUZ7IpuOAmQQ8u9/PgFDWbG5708/jK/Ti9OzExErhYVU1TMrCZqV7fY2Or2+uQ5H7zgPMNQF5HL7+upZeFeHLU//etrraM2YedQBAXsnoPXHn8yrcHVzE0LICWoECTQf+SG19dRqK3tMUxSRZtkIZtqbsblW+vF8/Pzy1XoNvdthjDas7He7swdno+JkM21v997r6A1MIKNBwiCiXssqhNsbcAwQosPTeek1MzBa947NmnewYcGR8Oh1sZG8s5LvQoACaK0Yegi+965MNWXnxIAx0AdyWnqDMjuh1KoIkYGNUPqh0zHaYcpwoin1ZiBFjFBVEWLm1ppzTkQAsAQYQdwhTap+dBCRXzkW6g2lIpdqRkYE1v1+AZgEuiRo8G2YaCBo75ZylSCdiMnoYPVefO1k8AYm7rlAjUDi7/ZN18HIffbp5zFO22Vf0bzXfsBQUu4DIJ6P1/t/XowB9RGl4Xj94M62zy+vou2sDUySyhJjTDXpAeL9YFKXaxYnuoL2BRfnzxghj7YKm4liHkszGkOUjtxIYjKM9ncpTnPr4+HDj8j+U5/B5R5k/vfv344m6rfXHb3NSqKvdrs5xKUWeN95nUd69Oi9vpQI0PtfTAn7eE/eLF0Y92eUjrcDISBmrBjbgkrIDPFYCfoCLPgflXOjuIEFgMYAAAAASUVORK5CYII='
}
},
- 'modern': {
+ modern: {
background: '#000000',
backgroundGrid: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAIAAAC0Ujn1AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpjZTg0NzU4MC00ODk3LTRkNjAtOWNhYi1mZTk1NzQ5NzhiNjkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTEzOEQwMDc5MDQyMTFFNDlBMzlFNzY4RjBCNkNENzMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTEzOEQwMDY5MDQyMTFFNDlBMzlFNzY4RjBCNkNENzMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDplNDRjOWZiNC0yNzE5LTQ3NDYtYmRmMi0wMmY2ZTA4ZjAxMmUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MzMwNTNEOTk5MDM1MTFFNDlBMzlFNzY4RjBCNkNENzMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7Y01+zAAAAMklEQVR42mJgGAWjYBSMgkEJGIlUd+j/WjjbjjGYGC1MtHP10DR6FIyCUTAKBikACDAA0NoDCLGGjH8AAAAASUVORK5CYII=',
primary: null,
@@ -44,7 +44,7 @@ window.BlockrainThemes = {
leftZag: '#fa1e1e'
}
},
- 'retro': {
+ retro: {
background: '#000000',
backgroundGrid: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAIAAAC0Ujn1AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpjZTg0NzU4MC00ODk3LTRkNjAtOWNhYi1mZTk1NzQ5NzhiNjkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTEzOEQwMDc5MDQyMTFFNDlBMzlFNzY4RjBCNkNENzMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTEzOEQwMDY5MDQyMTFFNDlBMzlFNzY4RjBCNkNENzMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDplNDRjOWZiNC0yNzE5LTQ3NDYtYmRmMi0wMmY2ZTA4ZjAxMmUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MzMwNTNEOTk5MDM1MTFFNDlBMzlFNzY4RjBCNkNENzMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7Y01+zAAAAMklEQVR42mJgGAWjYBSMgkEJGIlUd+j/WjjbjjGYGC1MtHP10DR6FIyCUTAKBikACDAA0NoDCLGGjH8AAAAASUVORK5CYII=',
primary: null,
@@ -61,7 +61,7 @@ window.BlockrainThemes = {
leftZag: '#fa1e1e'
}
},
- 'monochrome': {
+ monochrome: {
background: '#000000',
backgroundGrid: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAIAAAC0Ujn1AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpjZTg0NzU4MC00ODk3LTRkNjAtOWNhYi1mZTk1NzQ5NzhiNjkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTEzOEQwMDc5MDQyMTFFNDlBMzlFNzY4RjBCNkNENzMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTEzOEQwMDY5MDQyMTFFNDlBMzlFNzY4RjBCNkNENzMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDplNDRjOWZiNC0yNzE5LTQ3NDYtYmRmMi0wMmY2ZTA4ZjAxMmUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MzMwNTNEOTk5MDM1MTFFNDlBMzlFNzY4RjBCNkNENzMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7Y01+zAAAAMklEQVR42mJgGAWjYBSMgkEJGIlUd+j/WjjbjjGYGC1MtHP10DR6FIyCUTAKBikACDAA0NoDCLGGjH8AAAAASUVORK5CYII=',
primary: '#ffffff',
@@ -69,12 +69,12 @@ window.BlockrainThemes = {
stroke: '#000000',
innerStroke: '#000000'
},
- 'aerolab': {
+ aerolab: {
background: '#ffffff',
primary: '#ff7b00',
secondary: '#000000'
},
- 'gameboy': {
+ gameboy: {
background: '#C4CFA1',
primary: null,
secondary: null,
@@ -91,7 +91,7 @@ window.BlockrainThemes = {
leftZag: '#595F45'
}
},
- 'vim': {
+ vim: {
background: '#000000',
backgroundGrid: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAIAAAC0Ujn1AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpjZTg0NzU4MC00ODk3LTRkNjAtOWNhYi1mZTk1NzQ5NzhiNjkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTEzOEQwMDc5MDQyMTFFNDlBMzlFNzY4RjBCNkNENzMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTEzOEQwMDY5MDQyMTFFNDlBMzlFNzY4RjBCNkNENzMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDplNDRjOWZiNC0yNzE5LTQ3NDYtYmRmMi0wMmY2ZTA4ZjAxMmUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MzMwNTNEOTk5MDM1MTFFNDlBMzlFNzY4RjBCNkNENzMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7Y01+zAAAAMklEQVR42mJgGAWjYBSMgkEJGIlUd+j/WjjbjjGYGC1MtHP10DR6FIyCUTAKBikACDAA0NoDCLGGjH8AAAAASUVORK5CYII=',
primary: '#C2FFAE',
@@ -99,5 +99,5 @@ window.BlockrainThemes = {
stroke: '#000000',
strokeWidth: 3,
innerStroke: null
- },
-};
\ No newline at end of file
+ }
+};