Added image support for this Isometric Builder by Nicolas Gryman.
This pen is using the obelisk.js library.
| <header><h1><span>obelisk</span> buildr</h1> | |
| <aside id="auth"> | |
| <button class="login is-hidden"><span>log in</span><span class="icon icon-github"></span></button> | |
| <button class="logged is-hidden" title="connected to github"><span class="icon icon-github"></span></button> | |
| </aside> | |
| </header> | |
| <div id="help" class="pane"> | |
| <ul> | |
| <li><p><b>B</b>: Brush tool.</p></li> | |
| <li><p><b>E</b>: Erase tool.</p></li> | |
| <li><p><b>spacebar</b>: Color palette.</p></li> | |
| <li><p><b>←</b>, <b>→</b>: Rotate view.</p></li> | |
| <li><p><b>+</b>, <b>-</b>, <b>scroll</b>: Move draw layer up / down.</p></li> | |
| <li><p><b>CTRL + Z</b>: Undo.</p></li> | |
| <li><p><b>CTRL + S</b>: Save to gist.</p></li> | |
| <li><p><b>N</b>: New art.</p></li> | |
| <li><p><b>F</b>: Full canvas.</p></li> | |
| <li><p><b>H</b>: Toggle this thing.</p></li> | |
| </ul> | |
| </div> | |
| <main> | |
| <h2 class="select">Select an icon to be displayed using the <strong>ObeliskBuilder</strong></h2> | |
| <ul class="thumbnails"> | |
| <li class="active"><a href="#"><img src="data:image/gif;base64,R0lGODlhFAAUANUAAAAAAP///+/3997e1tbWzv/nIf/eEP/eGPfWGO/OGPfOAP/WCNa1EM6tEKWMEOfGGOe9EN61EMalELWUEO/GGL2UCKWECKWce9alCLWMCK2ECJRzCN6tEJx7EGNSGHNjMZyUe86cCL2MCKV7CIxrCGtSCKWchIxjANacCJxzCHNjOXtrQmtKCGNKGGtSIZSEY3tjOaWUe9bOxufn5////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAADQALAAAAAAUABQAAAbRwIBwSCwaiQKCadVqrUwEwVE4i5UcjASlYSnFZkeCqpNAHA4IRAKyURGKA5cEUajb1Y+MazAUvCZ0dnZoayMvUgEAiouMjYtCMYpCjpOLMUIwDI51jhUwQh4PB4yCjBAhHqAPZoKtBgoRqJgNga11aA8cIp8BMQ5ltgWEEBgpIEIyJRDAggcGCbkhLDJCfhYPZYwL0BHFh0NxExDYCQkKDxAcGBoefEQEHyQcERAQEeohJx9vRlUsKSIwoBCRgsWXKQEEyLigoomKCzIQIZxIJAgAOw==" alt=""></a></li> | |
| <li><a href="#"><img src="data:image/gif;base64,R0lGODlhFAAUAKIAAAAAAP///+3u7wByvP///wAAAAAAAAAAACH5BAEAAAQALAAAAAAUABQAAAMpOLrc/jDKOYS9VF2ct82VJ3kkx5Slg5LqurXu18Tmw3Yi/uo16P/AQQIAOw==" alt=""></a></li> | |
| <li><a href="#"><img src="data:image/gif;base64,R0lGODlhFAAUAPcAAORkE/F+GN1aHfOMMvqFDeelefWJIut6J/Gvg/upVthOF/aMKedtIeyBN//69eN7OvaLJul1KuiGPveJHOt1JfXh1fGBJ/3RpvOIKeeEOuaFOOCCQeWAOeNpIex5JdtVHd1bFvWGH/OFH//17fulTtlLC/7t3+52FOhxIviQK/qQJPeOK+WKQ/WLK+uKPuqJO/zMm+eHOe16JetxGuJlIf6aLvuWL/uULfqSK/iRLPiNJfKHKvKFJeaDOvGBKOWDOOSAN+V9POR+N+N9OeJ7OOt3I+RrGPuWLP3q2OBhH/mTLPqUK+eDPOiGO+58Ju93FuaidfjAk+F9N/Xj2+98JuymhfGodu14IOJ8NeWAPP/38Pi5gfmSK/SJKuJ5OPuUJ+SecO98IPqYNfR8CuFiGO98Hv3x6PioXfvIlvvRq/F5D9tTEvi3ff/37/e4hfuSJfbn4fSJKPe3f/KEKeR8O+55HfOUQPCEJvzy7f/+/vKaUu11Fe6ke/mLHOdqFeNfDeRpH+ZrHeSZZe59JvXj2falW/7s2/u/hut0GfecRvqdQfKCHPS0h/eHGeeIV+lyJN5lLvmVLO6TVfqTKd9hH/rj1vKeXOdsGutyH/yXLu12GPu9ge+BJu5wCvmSKfjWw+h9O/iGEvzAhPzr3+eGO/isaPbm3frawNtcIv3dwOF3NvWSN/iPK/mXNPehT+WBOuOCPvB4EuhvHt9TAd5YD99eGd1cH/qpWfysWv/+//B+IP7r2P3Wr+qbdffo3/R+DvKjad1XEvnXvPSOL/717t95NfmRJ+tpB+57JfuSJv7p0/qPIfyWK+KGRNlQF/CMQPqTLPe8ifSLKfa2gv7v4vzv5vGFKfKHKOWbaOWdbu96GeaHPfOVRvXKsfXMtfKGJ/fKqNxUGP7+/+iFTf///vGYVt5aEvaNKueFO/eydv338vvdxPuMGPe1fOdoEPu4dON+Nv/u3u+nfOF8PuVqHvmPJOBdEPKIKuR/OeWbZ+mHOumHO/fq4vzq2+99Jv///yH5BAAAAAAALAAAAAAUABQAAAj/AP/xgwJLCAcOr3qgQ9dE374XL7Zlm/LP1wYjgS5h8jDIgrUdXVakuGEjkwEpFQoYARShwQFEe8rcwRBnQQolS47UMCCoGQMAVsz0EzaN2yIRInEeOQItBSksj9wx+kf1n4M0iSZ4irSUywIMLuBRmFEO3ClqVbVsUvGF6YJvFlwAkSFDU6dY90qlqnrhjbFz1zhRkQDESR09bAoN+zXhHTGqFwzw8IHMA2EfT6JRNYGmFQES8ai2e+KkyCMmP3YEkFP1n6EEn0f8I/ZsBgoGqDHwWHXmEBKqbRKMSUcVnB96HbJoaCENQqNQk2CI+2digLZ1/9SBIkMjiIYV53SIuSGRjB0MqlGOASP3jw+tJN5z6FCkLBevehB+j/IQodo/b8EIQEcMN/QhSlWuqOHGP3lI8scn/1RiSzgPxGDDMrg48M8uA5xgCVXyzNLNP3hAosADLDBzxBu3bGGHLlc0gAAC45jjSBW9oOLMAwWIUAMOIQQQBjJFyAKAPbVQskYJJSgAQj6mbCACK13M4Y8HFDDQAQ1JCCDABx+AUEwF/8ABBgtNoJNBD6/gM8QQRBDhhSrzYEPIPwEBADs=" alt=""></a></li> | |
| <li><a href="#"><img src="data:image/gif;base64,R0lGODlhFAAUAOYAAAAAAP///8S0A0lDAUVAAUI9AePQBODOBN7MBNfFBM++BLKkA//qBf7pBf3oBfznBfvmBfjkBffjBfbiBfTgBfDcBe/bBerXBeXSBePRBeLQBd/NBcu6BMm5BMKyBLmqBJWJA5GFA42CA4h9A4Z7A/znBvvmBvTgBvPfBqmbBE1HAsW1BvTgCOvYCOjWCObUCODOB9jHB+zaCdnICMq6CMa3CKibB0tFA9vKCtLBCsCxCdfGC8+/DMa2C7aoCp6SCTYyA5mNCb2vDLytDLqsDLaoDMGyDYN5CbKkDaaaDaGVDYB2CjEuBJyQDZmNDXZtCl1WCCglBODNBOLPBV1VAvrlBu3ZBpiLBPbhB+7aB+PQB8m4Bsq5Bzs2AsCwB5GFB869C7ipCmtiBsu6DKudC7GiDZ+SDF5WB6SXDWRcCIp/DGZeCY2CDf8AAPr6+tXV1cvLy7CwsKysrIuLi4aGhnR0dG9vb11dXVhYWFNTUzMzMyUlJSAgIAwMDAUFBf///yH5BAEAAH8ALAAAAAAUABQAAAf/gAGCgm5yeHp+fnp4cm6Dj4JvegMgHxpSCyIFe3GQgnN9JBUUExMUFRcHV111kHVUChNVDw0NDxASFgYdKq2CcQQYEQ8AAAzHDcUWBx5AnW58KRPEgsYMANUXCSN8hQMVENiDxY8ACCtAcnchFA7iAHDF8QHxU1tid3wcE8lw9HLF5PwDoIHLlz1+MvC7BgfgMXgAUcCo8aOPnwPtHlrTWCXLDB1B+vARAK7BsZMnS2B5kSPMkT14RFyQQAslgwYmsMjA0YPMGUYFNliIAMDkTQdVWMgAAKZIkyiN+FzZYOVEsatXeQxBs6ZbADldvMTQ4qIFgDZtAOwYQySJGqiDOurcsEEjx46zaY0gUcIGiC9CdPqk8TFESLEySZykiWLHESRJTMQsMdPkCZQonDwNKpRHTzE9eRpBCgQAOw==" alt=""></a></li> | |
| <li><a href="#"><img src="data:image/gif;base64,R0lGODlhFAAUAOYAAAAAAP////3+//z+//X8/83y/9n1/+n5/zPM/zTM/zXM/zXN/zfN/znN/znO/zrO/z7P/0TQ/0XQ/0XR/0fR/0jR/0vS/03S/03T/1DT/1HT/1TU/1bV/2HY/2jZ/2ra/27b/3jd/3re/3ze/4Lg/4Tg/4Xh/5Hj/5Hk/5rm/53n/6Dn/6Ho/6Po/6nq/6vq/7Ps/7bt/7nt/7ru/7zu/7/v/8Dv/8Pw/8ny/8zy/83z/87z/9Dz/9H0/9P0/9b1/9r2/9/3/+P4/+b5/+r6/+v6/+/7//P8//f9//H8//L8//b9//n+//3//////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAE4ALAAAAAAUABQAAAeZgAGCg4SFhoeIiYdJPUOHTYg+FxEth0sjMQSERxgIDRsuhygIGitFgioIqqoshkEZqhQvSxWrqg0HhT4Oth0JtqomMzqENRbAyAgfPIQ2C8m2EiQ/hAI3GtCqNIgeDNAcTIJCSIQ4E8kMO4NDJyk0MCUQyQ8yhUQh2QgdzIc6IhUKVCWIAILGAEUBlAApkMOAEYQQI0qcSCgQADs=" alt=""></a></li> | |
| </ul> | |
| <div class="drop-zone"></div><span><-- or drop your 20x20 image here...</span> | |
| <br><h2 class="currently">Currently Displaying: </h2><img class="drop-image" src="" /> | |
| <canvas id="myCanvas" width="20" height="20"> | |
| <!-- Insert fallback content here --> | |
| </canvas> | |
| <div id="buildr"> | |
| <canvas id="scene"></canvas> | |
| </div> | |
| <div id="palette" class="is-hidden"> | |
| <button class="red" data-tool="brush" data-value="#c82829">1</button> | |
| <button class="orange" data-tool="brush" data-value="#f5871f">2</button> | |
| <button class="yellow" data-tool="brush" data-value="#eab700">3</button> | |
| <button class="green" data-tool="brush" data-value="#718c00">4</button> | |
| <button class="aqua" data-tool="brush" data-value="#3e999f">5</button> | |
| <button class="blue" data-tool="brush" data-value="#4271ae">6</button> | |
| <button class="purple" data-tool="brush" data-value="#8959a8">7</button> | |
| <button class="black" data-tool="brush" data-value="#4d4d4c">8</button> | |
| <button class="white is-active" data-tool="brush" data-value="#eeeeee">9</button> | |
| </div > | |
| <div id="overlay" class="is-hidden"></div> | |
| <div id="notification" class="is-hidden"></div> | |
| <div id="help" class="pane is-hidden"> | |
| <ul> | |
| <li><p><b>B</b>: Brush tool.</p></li> | |
| <li><p><b>E</b>: Erase tool.</p></li> | |
| <li><p><b>spacebar</b>: Color palette.</p></li> | |
| <li><p><b>←</b>, <b>→</b>: Rotate view.</p></li> | |
| <li><p><b>+</b>, <b>-</b>, <b>scroll</b>: Move draw layer up / down.</p></li> | |
| <li><p><b>CTRL + Z</b>: Undo.</p></li> | |
| <li><p><b>CTRL + S</b>: Save to gist.</p></li> | |
| <li><p><b>N</b>: New art.</p></li> | |
| <li><p><b>F</b>: Full canvas.</p></li> | |
| <li><p><b>H</b>: Toggle this thing.</p></li> | |
| </ul> | |
| </div> | |
| <div id="welcome" class="pane is-hidden"> | |
| <div class="content"> | |
| <h2>Welcome to obelisk buildr!</h2> | |
| <p>This little experiment makes use of the great <a target="_blank" href="https://github.com/nosir/obelisk.js">obelisk.js</a> library.</p> | |
| <ul> | |
| <li><p>Everything is driven by shortcuts here, no UI, I'm lazy :)<br> | |
| Press <b>H</b> to list all of them.</li> | |
| <li><p>You can draw on 20 layers of height. Scroll your mouse up or down to adjust elevation.</p></li> | |
| <li><p>Available tools are brush (<b>B</b>) and erase (<b>E</b>).</p></li> | |
| <li><p>Press the <b>spacebar</b> to spawn a 9 colors palette. Colors are also | |
| accessible form <b>1</b> to <b>9</b>. </p></li> | |
| <li><p>Rotate the view with <b>←</b> and <b>→</b> arrow keys</p></li> | |
| <li><p>Your work is automatically saved locally in the browser.<br> | |
| Press <b>CTRL + S</b> to save it to a github gist. | |
| If you log in, you will be able to fork and edit your own gists. | |
| If not, anonymous gists will be created each time you save! So please consider logging in.</p></li> | |
| <li><p>Press any key to continue...</p></li> | |
| </ul> | |
| </div> | |
| </div> | |
| </main> | |
| <footer> | |
| <a href="https://github.com/kostasx/obelisk-buildr/tree/image-dropper"><em>Github</em></a> | <a href="https://twitter.com/kostas_mns">Follow <strong>@kostas_mns</strong> on <em>Twitter</em></a> | |
| <a href="https://github.com/ngryman/obelisk-builder"> // Original Source on <em>Github</em></a> | | |
| <a href="http://twitter.com/ngryman">Follow <strong>@ngryman</strong> on <em>Twitter</em></a> | |
| </footer> |
Added image support for this Isometric Builder by Nicolas Gryman.
This pen is using the obelisk.js library.
| (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | |
| /*! | |
| * obelisk-builder | |
| * Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com> | |
| * MIT Licensed | |
| */ | |
| 'use strict'; | |
| // TODO: move this elsewhere | |
| obelisk.Point3D.prototype.clone = function() { | |
| return new obelisk.Point3D(this.x, this.y, this.z); | |
| }; | |
| /** | |
| * Module dependencies. | |
| */ | |
| var ui = require('./ui'), | |
| tool = require('./tool'), | |
| pointer = require('./pointer'), | |
| history = require('./history'), | |
| storage = require('./storage'); | |
| /** | |
| * Private variables | |
| */ | |
| var canvasEl = document.getElementById('scene'); | |
| var persistLock = false; | |
| var autoSaveTimeout; | |
| var resizeTimeout; | |
| var fullCanvased = false; | |
| /** | |
| * Module declaration. | |
| */ | |
| var app = {}; | |
| /** | |
| * Shift Detect | |
| */ | |
| app.shiftHandler = false; | |
| // Wow! What a bad, bad practice... Shame on me! | |
| app.createPixelArray = function( imageDataArray, width, height ){ | |
| var pixelArray = []; // Array( width * height ) | |
| var initial = 0; | |
| var tetarto = 0; | |
| var x = 0; | |
| var y = 0; | |
| var z = 0; | |
| $.each( imageDataArray.data, function( index, value ){ | |
| if ( index % 4 === 0 ){ | |
| pixelArray[initial] = { "x" : x, "y" : y, "z" : z, "c" : value.toString()[0], "rgba": [value] }; | |
| tetarto = 0; | |
| initial++; | |
| x++; | |
| if ( x % 20 === 0 && index !== 0 ){ | |
| // console.log(index, y); | |
| x = 0; | |
| y = ( y + 1 ); | |
| } | |
| } else { | |
| pixelArray[initial-1].rgba.push(value); | |
| tetarto++; | |
| } | |
| }); | |
| return pixelArray; | |
| }; | |
| app.getImageData = function( imageData, image, imageCtx ){ | |
| var pixelArray = this.createPixelArray( imageData, imageData.width, imageData.height ); | |
| pixelArray = { "colors":["0x333399","9999984","0xFFFFFF","15382272","15658734"], "data": pixelArray }; | |
| return pixelArray; | |
| }; | |
| /** | |
| * | |
| */ | |
| app.init = function() { | |
| ui.auth.init(); | |
| ui.scene.init(); | |
| ui.welcome.init(function() { | |
| ui.palette.init(); | |
| tool.init(ui.scene) | |
| .use('brush'); | |
| pointer.init(ui.scene) | |
| .click(tool.click) | |
| .drag(tool.drag); | |
| bindShortcuts(); | |
| bindScroll(); | |
| bindResize(); | |
| touchDisclaimer(); | |
| /* | |
| // fetches initial art | |
| storage.fetch(function(err, data, info) { | |
| if (err) return ui.notification.error(err); | |
| if (data) ui.scene.load(data); | |
| if (info) ui.notification.info(info); | |
| // auto save from now on | |
| ui.scene.changed(onAutoSave); | |
| }); | |
| */ | |
| }); | |
| loadImage( "data:image/gif;base64,R0lGODlhFAAUANUAAAAAAP///+/3997e1tbWzv/nIf/eEP/eGPfWGO/OGPfOAP/WCNa1EM6tEKWMEOfGGOe9EN61EMalELWUEO/GGL2UCKWECKWce9alCLWMCK2ECJRzCN6tEJx7EGNSGHNjMZyUe86cCL2MCKV7CIxrCGtSCKWchIxjANacCJxzCHNjOXtrQmtKCGNKGGtSIZSEY3tjOaWUe9bOxufn5////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAADQALAAAAAAUABQAAAbRwIBwSCwaiQKCadVqrUwEwVE4i5UcjASlYSnFZkeCqpNAHA4IRAKyURGKA5cEUajb1Y+MazAUvCZ0dnZoayMvUgEAiouMjYtCMYpCjpOLMUIwDI51jhUwQh4PB4yCjBAhHqAPZoKtBgoRqJgNga11aA8cIp8BMQ5ltgWEEBgpIEIyJRDAggcGCbkhLDJCfhYPZYwL0BHFh0NxExDYCQkKDxAcGBoefEQEHyQcERAQEeohJx9vRlUsKSIwoBCRgsWXKQEEyLigoomKCzIQIZxIJAgAOw==" ); | |
| // HANDLE THUMBNAIL CHANGE | |
| $(".thumbnails li").click(function(e){ | |
| var imgSrc = $(this).find("img").attr("src"); | |
| $(".thumbnails").find("li").removeClass('active'); | |
| $(this).addClass("active"); | |
| $("#myCanvas").show(); | |
| $(".drop-image").hide(); | |
| loadImage( imgSrc ); | |
| }); | |
| // HANDLE DRAG & DROP | |
| $('html').on('dragenter dragover', function(e) { e.stopPropagation(); }); | |
| $('.drop-image').on('load', function() { | |
| if ( this.naturalWidth > 20 || this.naturalHeight > 20 ) { | |
| console.log("IMAGE TOO LARGE"); | |
| } else { | |
| $("#myCanvas").hide(); | |
| $(".thumbnails li").removeClass("active"); | |
| loadImage( this.src ); | |
| $(this).show(); | |
| $('.drop-placeholder').hide(); | |
| } | |
| }); | |
| $('.drop-zone') | |
| .on('dragover', function() { | |
| $(this).addClass('drop-over'); | |
| return false; | |
| }) | |
| .on('dragleave', function() { | |
| $(this).removeClass('drop-over'); | |
| return false; | |
| }) | |
| .on('drop', function(e) { | |
| if(e.originalEvent.dataTransfer){ | |
| if(e.originalEvent.dataTransfer.files.length) { | |
| // $('.drop-image').hide(); | |
| $(this).removeClass('drop-over'); | |
| var file = e.originalEvent.dataTransfer.files[0]; | |
| if (!file || file.type.indexOf('image/') !== 0) { | |
| console.log("NOT AN IMAGE"); | |
| } else { | |
| if ( true ){ | |
| var reader = new FileReader(); | |
| reader.onload = function(e) { | |
| $('.drop-image').attr('src', e.target.result ); | |
| }; | |
| reader.readAsDataURL(file); | |
| } | |
| } | |
| e.preventDefault(); | |
| return false; | |
| } | |
| } | |
| }); | |
| $('.drop-image, .drop-samples img').on('dragstart', function(e) { | |
| e.preventDefault(); | |
| }); | |
| // logCurious(); | |
| }; | |
| /** | |
| * @private | |
| */ | |
| function loadImage(imgSrc){ | |
| var imageData; | |
| var imageCanvas = document.getElementById("myCanvas"); | |
| var imageCtx = imageCanvas.getContext("2d"); | |
| var image = new Image(); | |
| image.src = imgSrc; | |
| $(image).load(function() { | |
| imageCtx.drawImage(image, 0, 0, 20, 20); | |
| imageData = imageCtx.getImageData(0, 0, 20, 20); | |
| var output = app.getImageData( imageData, image, imageCtx ); | |
| storage.fetch(function(err, output, info) { | |
| if (err) return ui.notification.error(err); | |
| // if (output) ui.scene.load(output); | |
| if (output) ui.scene.load( output, "rgba" ); | |
| if (info) ui.notification.info(info); | |
| ui.scene.changed(onAutoSave); // auto save from now on | |
| }, output ); | |
| }); | |
| } | |
| /** | |
| * @private | |
| */ | |
| function logCurious() { | |
| console.log('Hey! Curious or having bugs?'); | |
| console.log('Please post ideas or issues here: https://github.com/ngryman/obelisk-buildr/issues.'); | |
| console.log('You can play with the window.scene object.'); | |
| if (console.table) { | |
| var methods = { | |
| snapshot: { description: 'Returns a base64 image of the current scene.' }, | |
| load: { description: 'Loads a scene. Same format as the art.json file in gists.' }, | |
| save: { description: 'Returns data associated with the current scene. Same format as the art.json file in gists.' } | |
| }; | |
| console.table(methods); | |
| } | |
| } | |
| /** | |
| * @private | |
| */ | |
| function bindShortcuts() { | |
| document.addEventListener('keydown', onKeyDown); | |
| document.addEventListener('keyup', onKeyUp); | |
| } | |
| /** | |
| * @private | |
| */ | |
| function bindScroll() { | |
| var isFirefox = /Firefox/i.test(navigator.userAgent); | |
| document.addEventListener(isFirefox ? 'DOMMouseScroll' : 'mousewheel', onScroll); | |
| } | |
| /** | |
| * @private | |
| */ | |
| function bindResize() { | |
| window.addEventListener('resize', onResize); | |
| } | |
| /** | |
| * @private | |
| */ | |
| function touchDisclaimer() { | |
| var isChrome = /chrome/i.exec(navigator.userAgent), | |
| isAndroid = /android/i.exec(navigator.userAgent), | |
| hasTouch = 'ontouchstart' in window && !(isChrome && !isAndroid); | |
| if (hasTouch) | |
| ui.notification.error('Hey! For now, there is no real support for touch devices. Yeah i know...'); | |
| } | |
| /** | |
| * @param {boolean} silent | |
| * @returns {object} | |
| * @private | |
| */ | |
| function save(silent) { | |
| var data = ui.scene.save(silent); | |
| storage.local(data); | |
| return data; | |
| } | |
| /** | |
| * @private | |
| */ | |
| function persist( local ) { | |
| if (persistLock) return; | |
| persistLock = true; | |
| // nothing to persist | |
| if (!ui.scene.changed() && !storage.orphan()) { | |
| persistLock = false; | |
| return ui.notification.error('nothing to save'); | |
| } | |
| // saves locally | |
| var data = save(); | |
| if ( local ) { | |
| console.log(JSON.stringify(data)); | |
| persistLock = false; | |
| return; | |
| } | |
| // persist to a gist | |
| storage.persist(data, function(err, info) { | |
| persistLock = false; | |
| if (err) return ui.notification.error(err); | |
| var anchor = '<a target="_blank" href="' + info.url + '">' + info.id + '</a>'; | |
| ui.notification.info(info.action + ' gist ' + anchor); | |
| }); | |
| } | |
| /** | |
| * @private | |
| */ | |
| function create() { | |
| storage.flush(); | |
| ui.scene.clear(); | |
| ui.notification.info('new craft!'); | |
| } | |
| /** | |
| * @private | |
| */ | |
| function fullCanvas() { | |
| var display = fullCanvased ? 'block' : 'none'; | |
| document.querySelector('header').style.display = display; | |
| document.querySelector('footer').style.display = display; | |
| var padding = fullCanvased ? '70px' : '0px'; | |
| document.querySelector('main').style.paddingTop = padding; | |
| document.querySelector('main').style.paddingBottom = padding; | |
| ui.scene.resize(); | |
| fullCanvased = !fullCanvased; | |
| } | |
| /** | |
| * @param {event} e | |
| * @private | |
| */ | |
| function onKeyDown(e) { | |
| switch (e.keyCode) { | |
| // + | |
| case 107: | |
| ui.scene.adjustFloor(+1); | |
| break; | |
| // - | |
| case 109: | |
| ui.scene.adjustFloor(-1); | |
| break; | |
| // left | |
| case 37: | |
| ui.scene.rotate(+1); | |
| break; | |
| // right | |
| case 39: | |
| ui.scene.rotate(-1); | |
| break; | |
| // b | |
| case 66: | |
| tool.use('brush'); | |
| break; | |
| // e | |
| case 69: | |
| tool.use('erase'); | |
| break; | |
| // space bar | |
| case 32: | |
| ui.palette.toggle(); | |
| break; | |
| // ctrl + z | |
| case 90: | |
| if (e.ctrlKey) history.back(); | |
| break; | |
| // ctrl + s | |
| case 83: | |
| e.preventDefault(); | |
| if (e.ctrlKey) { | |
| if (e.shiftKey){ | |
| persist( true ); | |
| } else { | |
| persist(); | |
| } | |
| } | |
| break; | |
| // n | |
| case 78: | |
| create(); | |
| break; | |
| // f | |
| case 70: | |
| fullCanvas(); | |
| break; | |
| // h | |
| case 72: | |
| e.preventDefault(); | |
| ui.help.toggle(); | |
| break; | |
| } | |
| // 123456789 | |
| if (e.keyCode >= 49 && e.keyCode <= 57) | |
| tool.use('brush').set(ui.palette.color(e.keyCode - 49)); | |
| // Shift Button Pressed | |
| if ( e.keyIdentifier === "Shift" ) | |
| app.shiftHandler = true; | |
| } | |
| /** | |
| * @param {event} e | |
| * @private | |
| */ | |
| function onKeyUp(e) { | |
| if ( e.keyIdentifier === "Shift" ) | |
| // Shift Button Unpressed | |
| app.shiftHandler = false; | |
| } | |
| /** | |
| * @param {event} e | |
| * @private | |
| */ | |
| function onScroll(e) { | |
| var delta = e.detail ? -e.detail : e.wheelDelta; | |
| ui.scene.adjustFloor(delta > 0 ? -1 : +1); | |
| } | |
| /** | |
| * @param {event} e | |
| * @private | |
| */ | |
| function onResize(e) { | |
| // hide on first event | |
| if (null == resizeTimeout) | |
| canvasEl.style.visibility = 'hidden'; | |
| // debounce scene resize | |
| clearTimeout(resizeTimeout); | |
| resizeTimeout = setTimeout(function() { | |
| ui.scene.resize(); | |
| canvasEl.style.visibility = 'visible'; | |
| resizeTimeout = null; | |
| }, 100); | |
| } | |
| /** | |
| * @private | |
| */ | |
| function onAutoSave() { | |
| // debounce auto save | |
| clearTimeout(autoSaveTimeout); | |
| autoSaveTimeout = setTimeout(save.bind(null, true), 1000); | |
| } | |
| /** | |
| * Global export. | |
| */ | |
| window.app = app; | |
| /** | |
| * Exports for hackers | |
| */ | |
| window.scene = ui.scene; | |
| },{"./history":3,"./pointer":4,"./storage":5,"./tool":6,"./ui":11}],2:[function(require,module,exports){ | |
| /*! | |
| * obelisk-builder | |
| * Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com> | |
| * MIT Licensed | |
| */ | |
| 'use strict'; | |
| /** | |
| * Private variables | |
| */ | |
| var anonymousMessage = 'View and edit it on http://ngryman.sh/obelisk-buildr.\n\n' + | |
| 'As this is an anonymous gist, you have to manually copy and paste its id, and append it' + | |
| 'to the url (i.e. http://ngryman.sh/obelisk-buildr#1337).'; | |
| var authenticatedMessage = 'View and edit it on http://ngryman.sh/obelisk-buildr/#{0}.'; | |
| var token; | |
| /** | |
| * Module declaration. | |
| */ | |
| var github = {}; | |
| /** | |
| * | |
| * @returns {boolean} | |
| */ | |
| github.authenticated = function() { | |
| return (null != token); | |
| }; | |
| /** | |
| * | |
| */ | |
| github.connect = function() { | |
| location.href = 'https://github.com/login/oauth/authorize?client_id=717772a46c3c521155d3&scope=gist'; | |
| }; | |
| /** | |
| * | |
| */ | |
| github.disconnect = function() { | |
| localStorage.removeItem('token'); | |
| token = null; | |
| }; | |
| /** | |
| * | |
| * @param content | |
| * @param callback | |
| */ | |
| github.newGist = function(content, callback) { | |
| var data = { | |
| description: 'An awesome pixel art chef d\'oeuvre', | |
| public: true, | |
| files: { | |
| 'data.json': { | |
| content: content | |
| } | |
| } | |
| }; | |
| // anonymous art.json | |
| if (!token) | |
| data.files['art.md'] = { | |
| content: anonymousMessage | |
| }; | |
| request('post', '/gists', data, token ? editMessage.bind(null, callback) : callback); | |
| }; | |
| /** | |
| * | |
| * @param id | |
| * @param content | |
| * @param callback | |
| */ | |
| github.editGist = function(id, content, callback) { | |
| var data = { | |
| files: { | |
| 'data.json': { | |
| content: content | |
| } | |
| } | |
| }; | |
| request('patch', '/gists/' + id, data, callback); | |
| }; | |
| /** | |
| * | |
| * @param id | |
| * @param callback | |
| */ | |
| github.forkGist = function(id, callback) { | |
| request('post', '/gists/' + id + '/forks', null, token ? editMessage.bind(null, callback) : callback); | |
| }; | |
| /** | |
| * | |
| * @param id | |
| * @param callback | |
| */ | |
| github.getGist = function(id, callback) { | |
| request('get', '/gists/' + id, null, callback); | |
| }; | |
| /** | |
| * | |
| * @param {function} callback | |
| * @param {object} err | |
| * @param {object} res | |
| * @private | |
| */ | |
| function editMessage(callback, err, res) { | |
| if (err) return callback(err); | |
| // art.json with the correct link. | |
| var data = { | |
| files: { | |
| 'art.md': { | |
| content: authenticatedMessage.replace('{0}', res.id) | |
| } | |
| } | |
| }; | |
| request('patch', '/gists/' + res.id, data, callback); | |
| } | |
| /** | |
| * @param {string} method | |
| * @param {string} uri | |
| * @param {object} data | |
| * @param {function} callback | |
| * @private | |
| */ | |
| function request(method, uri, data, callback) { | |
| var qs = ''; | |
| if (token) | |
| qs = '?access_token=' + token; | |
| reqwest({ | |
| url: 'https://api.github.com' + uri + qs, | |
| method: method, | |
| type: 'json', | |
| data: data ? JSON.stringify(data) : null, | |
| error: callback, | |
| success: callback.bind(null, null) | |
| }); | |
| } | |
| // TODO: github init | |
| /** | |
| * @private | |
| */ | |
| function fetchToken() { | |
| // priority from url | |
| if (~location.search.indexOf('?token=')) { | |
| token = location.search.match(/\?token=(.{40})/)[1]; | |
| localStorage.setItem('token', token); | |
| // remove token from query string | |
| location.replace(location.protocol + '//' + location.host + location.pathname); | |
| } | |
| // from local storage | |
| else | |
| token = localStorage.getItem('token'); | |
| } | |
| fetchToken(); | |
| /** | |
| * Exports. | |
| */ | |
| module.exports = github; | |
| },{}],3:[function(require,module,exports){ | |
| /*! | |
| * obelisk-builder | |
| * Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com> | |
| * MIT Licensed | |
| */ | |
| 'use strict'; | |
| /** | |
| * Private variables | |
| */ | |
| var stack = []; | |
| var sequence = null; | |
| /** | |
| * Module declaration. | |
| */ | |
| var history = {}; | |
| /** | |
| * | |
| * @param {function} fn | |
| */ | |
| history.push = function(fn) { | |
| if (!sequence) | |
| return stack.push(fn); | |
| sequence.push(fn); | |
| }; | |
| /** | |
| * | |
| */ | |
| history.startSequence = function() { | |
| sequence = []; | |
| }; | |
| /** | |
| * | |
| */ | |
| history.stopSequence = function() { | |
| var seq = sequence; | |
| stack.push(function() { | |
| seq.forEach(function(action) { | |
| action(); | |
| }); | |
| }); | |
| sequence = null; | |
| }; | |
| /** | |
| * | |
| * @returns {boolean} | |
| */ | |
| history.isSequenced = function() { | |
| return (null != sequence); | |
| }; | |
| /** | |
| * | |
| */ | |
| history.back = function() { | |
| if (0 === stack.length) return; | |
| stack.pop()(); | |
| }; | |
| /** | |
| * Exports. | |
| */ | |
| module.exports = history; | |
| },{}],4:[function(require,module,exports){ | |
| /*! | |
| * obelisk-builder | |
| * Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com> | |
| * MIT Licensed | |
| */ | |
| 'use strict'; | |
| /** | |
| * Module dependencies. | |
| */ | |
| var history = require('./history'); | |
| /** | |
| * Private variables | |
| */ | |
| var canvasEl = document.getElementById('scene'); | |
| var canvasOffset = new obelisk.Point(); | |
| var scene; | |
| var x, y;// TODO: screenPos | |
| var viewPoint = new obelisk.Point3D(); // TODO: viewPos | |
| var loopStarted = false; | |
| var loopStopIn = 0; | |
| var down = false; | |
| var dragging = false; | |
| var clickHandler; | |
| var dragHandler; | |
| var moveHandler; | |
| /** | |
| * Module declaration. | |
| */ | |
| var pointer = {}; | |
| /** | |
| * | |
| * @param {scene} _scene | |
| * @returns {pointer} | |
| */ | |
| pointer.init = function(_scene) { | |
| var main = document.querySelector('#buildr'); | |
| main.addEventListener('mousedown', onDown); | |
| main.addEventListener('mousemove', onMove); | |
| main.addEventListener('mouseup', onUp); | |
| main.addEventListener('mouseleave', onUp); | |
| window.addEventListener('resize', onResize); | |
| Loop.on('tick', apply); | |
| onResize(); | |
| scene = _scene; | |
| return pointer; | |
| }; | |
| /** | |
| * | |
| * @param {function} handler | |
| * @returns {pointer} | |
| */ | |
| pointer.click = function(handler) { | |
| clickHandler = handler; | |
| return pointer; | |
| }; | |
| /** | |
| * | |
| * @param {function} handler | |
| * @returns {pointer} | |
| */ | |
| pointer.drag = function(handler) { | |
| dragHandler = handler; | |
| return pointer; | |
| }; | |
| /** | |
| * | |
| * @param {function} handler | |
| * @returns {pointer} | |
| */ | |
| pointer.move = function(handler) { | |
| moveHandler = handler; | |
| return pointer; | |
| }; | |
| /** | |
| * @private | |
| */ | |
| function onDown() { | |
| down = true; | |
| } | |
| /** | |
| * @private | |
| */ | |
| function onUp() { | |
| // may happen when called by mouseleave | |
| if (!down) return; | |
| down = false; | |
| if (!dragging) { | |
| if (clickHandler) clickHandler(); | |
| return; | |
| } | |
| dragging = false; | |
| if (history.isSequenced()) | |
| history.stopSequence(); | |
| } | |
| /** | |
| * @param {event} e | |
| * @private | |
| */ | |
| function onMove(e) { | |
| x = e.pageX - canvasOffset.x; | |
| y = e.pageY - canvasOffset.y; | |
| if (down && !dragging) | |
| dragging = true; | |
| if (!loopStarted) { | |
| Loop.start(); | |
| loopStarted = true; | |
| } | |
| loopStopIn = 1; | |
| } | |
| /** | |
| * @private | |
| */ | |
| function onResize() { | |
| canvasOffset.x = canvasEl.offsetLeft; | |
| canvasOffset.y = canvasEl.offsetTop; | |
| } | |
| /** | |
| * @private | |
| */ | |
| function apply() { | |
| scene.screenToView(x, y, viewPoint); | |
| scene.select(viewPoint); | |
| if (dragging) { | |
| if (dragHandler) dragHandler(); | |
| return; | |
| } | |
| if (moveHandler) moveHandler(); | |
| if (0 === loopStopIn) { | |
| Loop.stop(); | |
| loopStarted = false; | |
| } | |
| loopStopIn--; | |
| } | |
| /** | |
| * Exports. | |
| */ | |
| module.exports = pointer; | |
| },{"./history":3}],5:[function(require,module,exports){ | |
| /*! | |
| * obelisk-builder | |
| * Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com> | |
| * MIT Licensed | |
| */ | |
| 'use strict'; | |
| /** | |
| * Module dependencies. | |
| */ | |
| var github = require('./github'); | |
| /** | |
| * Private variables | |
| */ | |
| var errors = { | |
| sadSave: "could not save your awesome art on the cloud :(", | |
| unknownGist: "it seems this gist does not exist anymore :(", | |
| invalidGist: "this gist does not behaves well, loading failed :(", | |
| forkFailed: "fork failed :(<br>does this gist still exists?" | |
| }; | |
| var gist; | |
| /** | |
| * Module declaration. | |
| */ | |
| var storage = {}; | |
| /** | |
| * | |
| * @param data | |
| */ | |
| storage.local = function(data) { | |
| localStorage.setItem(gist ? gist : 'orphan', JSON.stringify(data)); | |
| }; | |
| /** | |
| * | |
| * @returns {boolean} | |
| */ | |
| storage.orphan = function() { | |
| return (null == gist); | |
| }; | |
| /** | |
| * | |
| * @param {object} data | |
| * @param {function} callback | |
| */ | |
| storage.persist = function(data, callback) { | |
| var content = JSON.stringify(data, null, 2), | |
| action = 'created'; | |
| // working on an existing gist? | |
| if (gist) { | |
| // edit it | |
| github.editGist(gist, content, function(err, res) { | |
| // tried to edit a gist that is not ours | |
| if (err) { | |
| if (401 == err.status || 404 == err.status) { | |
| // fork it if we are authenticated | |
| // anonymous gist can't be edited after being forked | |
| if (github.authenticated()) { | |
| github.forkGist(gist, function(err, res) { | |
| if (err) return callback(errors.forkFailed); | |
| // then add changes | |
| action = 'forked gist ' + gist + ' to'; | |
| github.editGist(res.id, content, onData); | |
| }); | |
| } | |
| // if not, well, simply create it :p | |
| else | |
| github.newGist(content, onData); | |
| } | |
| else | |
| callback(errors.sadSave); | |
| return; | |
| } | |
| action = 'saved to'; | |
| onData(err, res); | |
| }); | |
| return; | |
| } | |
| // create a new one | |
| github.newGist(content, onData); | |
| function onData(err, res) { | |
| if (err) return callback(errors.sadSave); | |
| // remove orphan data | |
| if (storage.orphan()) | |
| localStorage.removeItem('orphan'); | |
| // set this gist as the current one | |
| gist = res.id; | |
| // and the last one we worked on | |
| localStorage.setItem('last', gist); | |
| // make sure the hash is up to date | |
| if (gist != location.hash.slice(1)) | |
| location.hash = gist; | |
| // save it locally too | |
| storage.local(data); | |
| var info = { | |
| id: res.id, | |
| url: res.html_url, | |
| action: action | |
| }; | |
| callback(null, info); | |
| } | |
| }; | |
| /** | |
| * | |
| * @param {function} callback | |
| */ | |
| storage.fetch = function(callback, data) { | |
| // LOAD LOCAL DATA ( STATISTICS ) | |
| // var data = { "colors":["7441408","9001384","13117481","15382272","15658734"], "data":[{"x":0,"y":0,"z":0,"c":2},{"x":0,"y":1,"z":0,"c":2},{"x":0,"y":1,"z":1,"c":2},{"x":0,"y":2,"z":0,"c":2},{"x":0,"y":2,"z":1,"c":2},{"x":0,"y":2,"z":2,"c":2},{"x":0,"y":2,"z":3,"c":2},{"x":0,"y":2,"z":4,"c":2},{"x":0,"y":2,"z":5,"c":2},{"x":0,"y":3,"z":0,"c":2},{"x":0,"y":3,"z":1,"c":2},{"x":0,"y":3,"z":2,"c":2},{"x":0,"y":3,"z":3,"c":2},{"x":0,"y":4,"z":0,"c":2},{"x":0,"y":4,"z":1,"c":2},{"x":0,"y":4,"z":2,"c":2},{"x":0,"y":5,"z":0,"c":2},{"x":0,"y":5,"z":1,"c":2},{"x":0,"y":5,"z":2,"c":2},{"x":0,"y":6,"z":0,"c":2},{"x":0,"y":6,"z":1,"c":2},{"x":0,"y":6,"z":2,"c":2},{"x":0,"y":6,"z":3,"c":2},{"x":0,"y":7,"z":0,"c":2},{"x":0,"y":7,"z":1,"c":2},{"x":0,"y":7,"z":2,"c":2},{"x":0,"y":7,"z":3,"c":2},{"x":0,"y":7,"z":4,"c":2},{"x":0,"y":7,"z":5,"c":2},{"x":0,"y":7,"z":6,"c":2},{"x":0,"y":8,"z":0,"c":2},{"x":0,"y":8,"z":1,"c":2},{"x":0,"y":8,"z":2,"c":2},{"x":0,"y":8,"z":3,"c":2},{"x":0,"y":8,"z":4,"c":2},{"x":0,"y":9,"z":0,"c":2},{"x":0,"y":9,"z":1,"c":2},{"x":0,"y":9,"z":2,"c":2},{"x":0,"y":9,"z":3,"c":2},{"x":0,"y":10,"z":0,"c":2},{"x":0,"y":10,"z":1,"c":2},{"x":0,"y":10,"z":2,"c":2},{"x":0,"y":11,"z":0,"c":2},{"x":0,"y":11,"z":1,"c":2},{"x":0,"y":12,"z":0,"c":2},{"x":0,"y":12,"z":1,"c":2},{"x":0,"y":12,"z":2,"c":2},{"x":0,"y":13,"z":0,"c":2},{"x":0,"y":13,"z":1,"c":2},{"x":0,"y":13,"z":2,"c":2},{"x":0,"y":13,"z":3,"c":2},{"x":0,"y":13,"z":4,"c":2},{"x":0,"y":14,"z":0,"c":2},{"x":0,"y":14,"z":1,"c":2},{"x":0,"y":14,"z":2,"c":2},{"x":0,"y":14,"z":3,"c":2},{"x":0,"y":14,"z":4,"c":2},{"x":0,"y":14,"z":5,"c":2},{"x":0,"y":15,"z":0,"c":2},{"x":0,"y":15,"z":1,"c":2},{"x":0,"y":15,"z":2,"c":2},{"x":0,"y":15,"z":3,"c":2},{"x":0,"y":15,"z":4,"c":2},{"x":0,"y":16,"z":0,"c":2},{"x":0,"y":16,"z":1,"c":2},{"x":0,"y":16,"z":2,"c":2},{"x":0,"y":16,"z":3,"c":2},{"x":0,"y":17,"z":0,"c":2},{"x":0,"y":17,"z":1,"c":2},{"x":0,"y":17,"z":2,"c":2},{"x":0,"y":18,"z":0,"c":2},{"x":0,"y":18,"z":1,"c":2},{"x":0,"y":19,"z":0,"c":2},{"x":4,"y":0,"z":0,"c":4},{"x":5,"y":0,"z":0,"c":0},{"x":5,"y":0,"z":1,"c":0},{"x":5,"y":0,"z":2,"c":0},{"x":5,"y":1,"z":0,"c":0},{"x":5,"y":1,"z":1,"c":0},{"x":5,"y":1,"z":2,"c":0},{"x":5,"y":1,"z":3,"c":0},{"x":5,"y":1,"z":4,"c":0},{"x":5,"y":2,"z":0,"c":0},{"x":5,"y":2,"z":1,"c":0},{"x":5,"y":2,"z":2,"c":0},{"x":5,"y":3,"z":0,"c":0},{"x":5,"y":3,"z":1,"c":0},{"x":5,"y":4,"z":0,"c":0},{"x":5,"y":5,"z":0,"c":0},{"x":5,"y":6,"z":0,"c":0},{"x":5,"y":7,"z":0,"c":0},{"x":5,"y":7,"z":1,"c":0},{"x":5,"y":8,"z":0,"c":0},{"x":5,"y":8,"z":1,"c":0},{"x":5,"y":9,"z":0,"c":0},{"x":5,"y":9,"z":1,"c":0},{"x":5,"y":9,"z":2,"c":0},{"x":5,"y":10,"z":0,"c":0},{"x":5,"y":10,"z":1,"c":0},{"x":5,"y":10,"z":2,"c":0},{"x":5,"y":10,"z":3,"c":0},{"x":5,"y":11,"z":0,"c":0},{"x":5,"y":11,"z":1,"c":0},{"x":5,"y":11,"z":2,"c":0},{"x":5,"y":11,"z":3,"c":0},{"x":5,"y":11,"z":4,"c":0},{"x":5,"y":11,"z":5,"c":0},{"x":5,"y":11,"z":6,"c":0},{"x":5,"y":11,"z":7,"c":0},{"x":5,"y":12,"z":0,"c":0},{"x":5,"y":12,"z":1,"c":0},{"x":5,"y":12,"z":2,"c":0},{"x":5,"y":12,"z":3,"c":0},{"x":5,"y":13,"z":0,"c":0},{"x":5,"y":13,"z":1,"c":0},{"x":5,"y":13,"z":2,"c":0},{"x":5,"y":14,"z":0,"c":0},{"x":5,"y":14,"z":1,"c":0},{"x":5,"y":14,"z":2,"c":0},{"x":5,"y":14,"z":3,"c":0},{"x":5,"y":15,"z":0,"c":0},{"x":5,"y":15,"z":1,"c":0},{"x":5,"y":15,"z":2,"c":0},{"x":5,"y":16,"z":0,"c":0},{"x":5,"y":16,"z":1,"c":0},{"x":5,"y":17,"z":0,"c":0},{"x":5,"y":17,"z":1,"c":0},{"x":5,"y":18,"z":0,"c":0},{"x":5,"y":18,"z":1,"c":0},{"x":5,"y":19,"z":0,"c":0},{"x":10,"y":1,"z":0,"c":3},{"x":10,"y":1,"z":1,"c":3},{"x":10,"y":1,"z":2,"c":3},{"x":10,"y":2,"z":0,"c":3},{"x":10,"y":2,"z":1,"c":3},{"x":10,"y":3,"z":0,"c":3},{"x":10,"y":3,"z":1,"c":3},{"x":10,"y":4,"z":0,"c":3},{"x":10,"y":4,"z":1,"c":3},{"x":10,"y":4,"z":2,"c":3},{"x":10,"y":4,"z":3,"c":3},{"x":10,"y":5,"z":0,"c":3},{"x":10,"y":5,"z":1,"c":3},{"x":10,"y":5,"z":2,"c":3},{"x":10,"y":6,"z":0,"c":3},{"x":10,"y":6,"z":1,"c":3},{"x":10,"y":6,"z":2,"c":3},{"x":10,"y":7,"z":0,"c":3},{"x":10,"y":7,"z":1,"c":3},{"x":10,"y":7,"z":2,"c":3},{"x":10,"y":8,"z":0,"c":3},{"x":10,"y":8,"z":1,"c":3},{"x":10,"y":8,"z":2,"c":3},{"x":10,"y":9,"z":0,"c":3},{"x":10,"y":9,"z":1,"c":3},{"x":10,"y":10,"z":0,"c":3},{"x":10,"y":11,"z":0,"c":3},{"x":10,"y":12,"z":0,"c":3},{"x":10,"y":12,"z":1,"c":3},{"x":10,"y":13,"z":0,"c":3},{"x":10,"y":13,"z":1,"c":3},{"x":10,"y":13,"z":2,"c":3},{"x":10,"y":14,"z":0,"c":3},{"x":10,"y":14,"z":1,"c":3},{"x":10,"y":14,"z":2,"c":3},{"x":10,"y":14,"z":3,"c":3},{"x":10,"y":15,"z":0,"c":3},{"x":10,"y":15,"z":1,"c":3},{"x":10,"y":15,"z":2,"c":3},{"x":10,"y":16,"z":0,"c":3},{"x":10,"y":16,"z":1,"c":3},{"x":10,"y":17,"z":0,"c":3},{"x":10,"y":18,"z":0,"c":3},{"x":10,"y":19,"z":0,"c":3},{"x":15,"y":0,"z":0,"c":1},{"x":15,"y":0,"z":1,"c":1},{"x":15,"y":0,"z":2,"c":1},{"x":15,"y":1,"z":0,"c":1},{"x":15,"y":1,"z":1,"c":1},{"x":15,"y":2,"z":0,"c":1},{"x":15,"y":3,"z":0,"c":1},{"x":15,"y":3,"z":1,"c":1},{"x":15,"y":3,"z":2,"c":1},{"x":15,"y":3,"z":3,"c":1},{"x":15,"y":4,"z":0,"c":1},{"x":15,"y":5,"z":0,"c":1},{"x":15,"y":6,"z":0,"c":1},{"x":15,"y":6,"z":1,"c":1},{"x":15,"y":7,"z":0,"c":1},{"x":15,"y":8,"z":0,"c":1},{"x":15,"y":9,"z":0,"c":1},{"x":15,"y":9,"z":1,"c":1},{"x":15,"y":9,"z":2,"c":1},{"x":15,"y":9,"z":3,"c":1},{"x":15,"y":9,"z":4,"c":1},{"x":15,"y":10,"z":0,"c":1},{"x":15,"y":10,"z":1,"c":1},{"x":15,"y":10,"z":2,"c":1},{"x":15,"y":11,"z":0,"c":1},{"x":15,"y":11,"z":1,"c":1},{"x":15,"y":12,"z":0,"c":1},{"x":15,"y":12,"z":1,"c":1},{"x":15,"y":13,"z":0,"c":1},{"x":15,"y":14,"z":0,"c":1},{"x":15,"y":15,"z":0,"c":1},{"x":15,"y":16,"z":0,"c":1},{"x":15,"y":16,"z":1,"c":1},{"x":15,"y":16,"z":2,"c":1},{"x":15,"y":16,"z":3,"c":1},{"x":15,"y":16,"z":4,"c":1},{"x":15,"y":16,"z":5,"c":1},{"x":15,"y":16,"z":6,"c":1},{"x":15,"y":17,"z":0,"c":1},{"x":15,"y":17,"z":1,"c":1},{"x":15,"y":17,"z":2,"c":1},{"x":15,"y":17,"z":3,"c":1},{"x":15,"y":18,"z":0,"c":1},{"x":15,"y":18,"z":1,"c":1},{"x":15,"y":18,"z":2,"c":1},{"x":15,"y":19,"z":0,"c":1}]}; | |
| // LOAD DATA FROM IMAGE | |
| return callback(null, JSON.parse(JSON.stringify(data)), 'LOADED DATA FROM IMAGE'); | |
| /* CODE TEMPORARILY DISABLED | |
| // priority to url | |
| if (location.hash) { | |
| gist = location.hash.slice(1); | |
| // if it was a gist we were working on, load local data as it is more fresh | |
| data = localStorage.getItem(gist); | |
| if (data) | |
| return callback(null, JSON.parse(data), 'loaded your last local changes for ' + gist); | |
| // if not loads the gist | |
| github.getGist(gist, function(err, res) { | |
| if (err) return callback(errors.unknownGist); | |
| var data; | |
| try { | |
| data = JSON.parse(res.files['data.json'].content); | |
| } | |
| catch (e) { | |
| return callback(errors.invalidGist); | |
| } | |
| callback(null, data); | |
| }); | |
| return; | |
| } | |
| // well, load the last working gist? | |
| gist = localStorage.getItem('last'); | |
| data = localStorage.getItem(gist); | |
| if (data) { | |
| location.hash = gist; | |
| return callback(null, JSON.parse(data), 'loaded your last saved stuff'); | |
| } | |
| // ok ok, load the last orphan art? | |
| data = localStorage.getItem('orphan'); | |
| if (data) return callback(null, JSON.parse(data), 'loaded your last local stuff'); | |
| // i can't do nothing more dude, new one! | |
| callback(null, null, 'new'); | |
| */ | |
| }; | |
| /** | |
| * | |
| */ | |
| storage.flush = function() { | |
| localStorage.removeItem('last'); | |
| location.hash = ''; | |
| }; | |
| module.exports = storage; | |
| },{"./github":2}],6:[function(require,module,exports){ | |
| /*! | |
| * obelisk-builder | |
| * Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com> | |
| * MIT Licensed | |
| */ | |
| 'use strict'; | |
| /** | |
| * Module dependencies. | |
| */ | |
| var tools = { | |
| brush: require('./tools/brush'), | |
| erase: require('./tools/erase') | |
| }; | |
| /** | |
| * Private variables | |
| */ | |
| var scene; | |
| var current; | |
| /** | |
| * Module declaration. | |
| */ | |
| var tool = {}; | |
| /** | |
| * | |
| * @param _scene | |
| * @returns {tool} | |
| */ | |
| tool.init = function(_scene) { | |
| scene = _scene; | |
| return tool; | |
| }; | |
| /** | |
| * | |
| * @param type | |
| * @returns {tool} | |
| */ | |
| tool.use = function(type) { | |
| current = tools[type]; | |
| return tool; | |
| }; | |
| /** | |
| * | |
| * @param value | |
| * @returns {tool} | |
| */ | |
| tool.set = function(value) { | |
| if (current.set) current.set(value); | |
| return tool; | |
| }; | |
| /** | |
| * | |
| * @returns {tool} | |
| */ | |
| tool.click = function() { | |
| current.click(scene); | |
| return tool; | |
| }; | |
| /** | |
| * | |
| * @returns {tool} | |
| */ | |
| tool.drag = function() { | |
| current.drag(scene); | |
| return tool; | |
| }; | |
| /** | |
| * Exports. | |
| */ | |
| module.exports = tool; | |
| },{"./tools/brush":7,"./tools/erase":8}],7:[function(require,module,exports){ | |
| /*! | |
| * obelisk-builder | |
| * Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com> | |
| * MIT Licensed | |
| */ | |
| 'use strict'; | |
| /** | |
| * Module dependencies. | |
| */ | |
| var history = require('../history'); | |
| /** | |
| * Private variables | |
| */ | |
| var color = obelisk.ColorPattern.GRAY; | |
| /** | |
| * Module declaration. | |
| */ | |
| var brush = {}; | |
| /** | |
| * | |
| * @param {string} value | |
| */ | |
| brush.set = function(value) { | |
| // strip # | |
| value = value.slice(1); | |
| // convert to number | |
| value = parseInt(value, 16); | |
| color = value; | |
| }; | |
| /** | |
| * | |
| * @param {scene} scene | |
| */ | |
| brush.click = function(scene) { | |
| if (scene.add(color)) | |
| history.push(scene.remove.bind(scene, scene.selected().clone())); | |
| }; | |
| /** | |
| * | |
| * @param {scene} scene | |
| */ | |
| brush.drag = function(scene) { | |
| if (scene.add(color)) { | |
| if (!history.isSequenced()) | |
| history.startSequence(); | |
| history.push(scene.remove.bind(scene, scene.selected().clone())); | |
| } | |
| }; | |
| /** | |
| * Exports. | |
| */ | |
| module.exports = brush; | |
| },{"../history":3}],8:[function(require,module,exports){ | |
| /*! | |
| * obelisk-builder | |
| * Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com> | |
| * MIT Licensed | |
| */ | |
| 'use strict'; | |
| /** | |
| * Module dependencies. | |
| */ | |
| var history = require('../history'); | |
| /** | |
| * Module declaration. | |
| */ | |
| var erase = {}; | |
| /** | |
| * | |
| * @param {scene} scene | |
| */ | |
| erase.click = function(scene) { | |
| var color = scene.color(); | |
| if (scene.remove()) | |
| history.push(scene.add.bind(scene, | |
| color, | |
| scene.selected().clone() | |
| )); | |
| }; | |
| /** | |
| * | |
| * @param {scene} scene | |
| */ | |
| erase.drag = function(scene) { | |
| var color = scene.color(); | |
| if (scene.remove()) { | |
| if (!history.isSequenced()) | |
| history.startSequence(); | |
| history.push(scene.add.bind(scene, | |
| color, | |
| scene.selected().clone() | |
| )); | |
| } | |
| }; | |
| /** | |
| * Exports. | |
| */ | |
| module.exports = erase; | |
| },{"../history":3}],9:[function(require,module,exports){ | |
| /*! | |
| * obelisk-builder | |
| * Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com> | |
| * MIT Licensed | |
| */ | |
| 'use strict'; | |
| /** | |
| * Module dependencies. | |
| */ | |
| var github = require('./../github'); | |
| /** | |
| * Private variables | |
| */ | |
| var loginEl = document.querySelector('#auth .login'); | |
| var loggedEl = document.querySelector('#auth .logged'); | |
| /** | |
| * Module declaration. | |
| */ | |
| var auth = {}; | |
| /** | |
| * | |
| */ | |
| auth.init = function() { | |
| // login button | |
| loginEl.addEventListener('click', onLoginBtnClick); | |
| // logged button | |
| loggedEl.addEventListener('click', onLoggedBtnClick); | |
| // initial ui state | |
| if (github.authenticated()) | |
| loggedEl.classList.remove('is-hidden'); | |
| else | |
| loginEl.classList.remove('is-hidden'); | |
| }; | |
| /** | |
| * @private | |
| */ | |
| function toggle() { | |
| loginEl.classList.toggle('is-hidden'); | |
| loggedEl.classList.toggle('is-hidden'); | |
| } | |
| /** | |
| * @param {event} e | |
| * @private | |
| */ | |
| function onLoginBtnClick(e) { | |
| if (this.classList.contains('is-disabled')) return; | |
| this.querySelector('span:first-child').innerText = 'wait for it...'; | |
| this.classList.add('is-disabled'); | |
| github.connect(); | |
| } | |
| /** | |
| * @param {event} e | |
| * @private | |
| */ | |
| function onLoggedBtnClick(e) { | |
| var res = confirm('Disconnect from Github?'); | |
| if (!res) return; | |
| github.disconnect(); | |
| toggle(); | |
| // avoids spacebar to trigger it again | |
| this.blur(); | |
| } | |
| /** | |
| * Exports. | |
| */ | |
| module.exports = auth; | |
| },{"./../github":2}],10:[function(require,module,exports){ | |
| /*! | |
| * obelisk-builder | |
| * Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com> | |
| * MIT Licensed | |
| */ | |
| 'use strict'; | |
| /** | |
| * Private variables | |
| */ | |
| var el = document.querySelector('#help'); | |
| var overlayEl = document.getElementById('overlay'); | |
| /** | |
| * Module declaration. | |
| */ | |
| var help = {}; | |
| help.toggle = function() { | |
| el.classList.toggle('is-hidden'); | |
| overlayEl.classList.toggle('is-hidden'); | |
| }; | |
| /** | |
| * Exports. | |
| */ | |
| module.exports = help; | |
| },{}],11:[function(require,module,exports){ | |
| /*! | |
| * obelisk-builder | |
| * Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com> | |
| * MIT Licensed | |
| */ | |
| 'use strict'; | |
| /** | |
| * Exports. | |
| */ | |
| module.exports = { | |
| auth: require('./auth'), | |
| notification: require('./notification'), | |
| palette: require('./palette'), | |
| scene: require('./scene'), | |
| welcome: require('./welcome'), | |
| help: require('./help') | |
| }; | |
| },{"./auth":9,"./help":10,"./notification":12,"./palette":13,"./scene":14,"./welcome":15}],12:[function(require,module,exports){ | |
| /*! | |
| * obelisk-builder | |
| * Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com> | |
| * MIT Licensed | |
| */ | |
| 'use strict'; | |
| /** | |
| * Private variables | |
| */ | |
| var el = document.getElementById('notification'); | |
| var timeout = null; | |
| var queue = []; | |
| var currentMessage; | |
| var count = 0; | |
| /** | |
| * Module declaration. | |
| */ | |
| var notification = {}; | |
| /** | |
| * | |
| * @param {string} type | |
| * @param {string} message | |
| */ | |
| notification.push = function(type, message) { | |
| // a message is being displayed, enqueue | |
| if (timeout) { | |
| // is it the same message? | |
| // if so don't enqueue, add multiplier | |
| if (message == currentMessage) { | |
| el.innerHTML = message + ' (' + (++count) + ')'; | |
| return; | |
| } | |
| // else enqueue | |
| return queue.push({ | |
| type: type, | |
| message: message | |
| }); | |
| } | |
| display(type, message); | |
| }; | |
| /** Shortcuts */ | |
| notification.info = notification.push.bind(null, 'info'); | |
| notification.error = notification.push.bind(null, 'error'); | |
| /** | |
| * @param {string} type | |
| * @param {string} message | |
| * @private | |
| */ | |
| function display(type, message) { | |
| el.innerHTML = message; | |
| el.dataset.type = type; | |
| el.classList.remove('is-hidden'); | |
| timeout = setTimeout(onHide, 5000); | |
| currentMessage = message; | |
| } | |
| /** | |
| * @private | |
| */ | |
| function onHide() { | |
| el.classList.add('is-hidden'); | |
| // messages in the queue, pop | |
| if (queue.length > 0) { | |
| var notif = queue.pop(); | |
| setTimeout(display.bind(null, | |
| notif.type, | |
| notif.message | |
| ), 300); | |
| return; | |
| } | |
| timeout = null; | |
| currentMessage = null; | |
| count = 0; | |
| } | |
| /** | |
| * Exports. | |
| */ | |
| module.exports = notification; | |
| },{}],13:[function(require,module,exports){ | |
| /*! | |
| * obelisk-builder | |
| * Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com> | |
| * MIT Licensed | |
| */ | |
| 'use strict'; | |
| /** | |
| * Module dependencies. | |
| */ | |
| var tool = require('./../tool'); | |
| /** | |
| * Private variables | |
| */ | |
| var el = document.getElementById('palette'); | |
| var overlayEl = document.getElementById('overlay'); | |
| var active = document.querySelector('#palette .is-active'); | |
| var visible = false; | |
| var x, y; | |
| var width, height; | |
| /** | |
| * Module declaration. | |
| */ | |
| var palette = {}; | |
| /** | |
| * | |
| */ | |
| palette.init = function() { | |
| var style = getComputedStyle(el); | |
| width = parseInt(style.width); | |
| height = parseInt(style.height); | |
| window.addEventListener('mousemove', function(e) { | |
| x = e.clientX; | |
| y = e.clientY; | |
| }); | |
| el.addEventListener('click', function(e) { | |
| var btn = e.target; | |
| if ('BUTTON' != btn.tagName) return; | |
| if (active) | |
| active.classList.remove('is-active'); | |
| btn.classList.add('is-active'); | |
| // tool selection and value | |
| tool.use(btn.dataset.tool).set(btn.dataset.value); | |
| active = e.target; | |
| }); | |
| }; | |
| /** | |
| * | |
| */ | |
| palette.toggle = function() { | |
| if (!visible) { | |
| el.style.left = (x - width / 2) + 'px'; | |
| el.style.top = (y - height / 2) + 'px'; | |
| } | |
| el.classList.toggle('is-hidden'); | |
| overlayEl.classList.toggle('is-hidden'); | |
| visible = !visible; | |
| }; | |
| /** | |
| * | |
| * @param {number} index | |
| * @returns {string} | |
| */ | |
| palette.color = function(index) { | |
| var btn = el.querySelector('button:nth-child(' + (index + 1) + ')'); | |
| return btn.dataset.value; | |
| }; | |
| /** | |
| * Exports. | |
| */ | |
| module.exports = palette; | |
| },{"./../tool":6}],14:[function(require,module,exports){ | |
| /*! | |
| * obelisk-builder | |
| * Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com> | |
| * MIT Licensed | |
| */ | |
| 'use strict'; | |
| /** | |
| * Private variables | |
| */ | |
| var canvasEl = document.getElementById('scene'); | |
| var SIZE = 20; | |
| var BLOCK_SIZE = 20; | |
| var view; | |
| var floor; | |
| var cubes; | |
| var blocks; | |
| var origin; | |
| var changed = false; | |
| var changedHandler; | |
| /** | |
| * Module declaration. | |
| */ | |
| var scene = {}; | |
| /** | |
| * | |
| * @returns {scene} | |
| */ | |
| scene.init = function() { | |
| cubes = {}; | |
| cubes[obelisk.ColorPattern.GRAY] = createCube(BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE, obelisk.ColorPattern.GRAY); | |
| floor = { | |
| normal: createBrick(BLOCK_SIZE, BLOCK_SIZE, '0xFF222222'), | |
| elevated: createBrick(BLOCK_SIZE + 2, BLOCK_SIZE + 2, '0xFF222222'), | |
| highlighted: createBrick(BLOCK_SIZE, BLOCK_SIZE, '0xFF444444'), | |
| elevatedHighlighted: createBrick(BLOCK_SIZE + 2, BLOCK_SIZE + 2, '0xFF555555'), | |
| selected: new obelisk.Point3D(-1, -1, 0), | |
| offset: 0 | |
| }; | |
| blocks = matrix(SIZE); | |
| scene.resize(); | |
| return scene; | |
| }; | |
| /** | |
| * | |
| */ | |
| scene.resize = function() { | |
| // 1:1 ratio | |
| var style = getComputedStyle(canvasEl); | |
| canvasEl.width = parseInt(style.width); | |
| canvasEl.height = parseInt(style.height); | |
| origin = new obelisk.Point(canvasEl.width / 2, canvasEl.height / 2); | |
| view = new obelisk.PixelView(canvasEl, new obelisk.Point( | |
| origin.x, origin.y | |
| )); | |
| var oldBlockSize = BLOCK_SIZE; | |
| // small screens | |
| if (canvasEl.width < 800 || canvasEl.height < 800) { | |
| var size = Math.min(canvasEl.width, canvasEl.height); | |
| // compute new block size | |
| BLOCK_SIZE = Math.floor(size / 40); | |
| // ensure it's even (obelisk needs it) | |
| BLOCK_SIZE = BLOCK_SIZE - (BLOCK_SIZE % 2); | |
| } | |
| // ensure block size comes back to its original size | |
| else | |
| BLOCK_SIZE = 20; | |
| // adapt existing geom to the correct size | |
| if (oldBlockSize != BLOCK_SIZE) { | |
| for (var color in cubes) { | |
| if (!cubes.hasOwnProperty(color)) continue; | |
| cubes[color] = createCube(BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE, parseInt(color)); | |
| } | |
| floor.normal = createBrick(BLOCK_SIZE, BLOCK_SIZE, '0xFF222222'); | |
| floor.elevated = createBrick(BLOCK_SIZE + 2, BLOCK_SIZE + 2, '0xFF222222'); | |
| floor.highlighted = createBrick(BLOCK_SIZE, BLOCK_SIZE, '0xFF444444'); | |
| floor.elevatedHighlighted = createBrick(BLOCK_SIZE + 2, BLOCK_SIZE + 2, '0xFF555555'); | |
| } | |
| scene.draw(); | |
| }; | |
| /** | |
| * | |
| */ | |
| scene.draw = function() { | |
| view.clear(); | |
| drawBlocks(view, blocks, cubes, 0, floor.offset); | |
| drawFloor(view, floor); | |
| drawBlocks(view, blocks, cubes, floor.offset, SIZE); | |
| }; | |
| /** | |
| * | |
| * @param {number} x | |
| * @param {number} y | |
| * @param {obelisk.Point3D} point | |
| */ | |
| scene.screenToView = function(x, y, point) { | |
| x -= origin.x; | |
| y -= origin.y; | |
| point.x = Math.floor(((x + 2 * y) / 2) / BLOCK_SIZE) + floor.offset; | |
| point.y = Math.floor(((-x + 2 * y) / 2) / BLOCK_SIZE) + floor.offset; | |
| point.z = floor.offset; | |
| }; | |
| /** | |
| * | |
| * @param {obelisk.Point3D} point | |
| */ | |
| scene.select = function(point) { | |
| clampBounds(point); | |
| floor.selected.x = point.x; | |
| floor.selected.y = point.y; | |
| floor.selected.z = point.z; | |
| scene.draw(); | |
| }; | |
| /** | |
| * | |
| * @returns {obelisk.Point} | |
| */ | |
| scene.selected = function() { | |
| return floor.selected; | |
| }; | |
| /** | |
| * | |
| * @param {obelisk.Point3D} point | |
| * @returns {number|null} | |
| */ | |
| scene.color = function(point) { | |
| point = point || scene.selected(); | |
| var block = blocks[point.x][point.y][point.z]; | |
| if (!block) return null; | |
| return block.color; | |
| }; | |
| /** | |
| * | |
| * @param {number} color | |
| * @param {obelisk.Point3D} point | |
| * @returns {boolean} | |
| */ | |
| scene.add = function(color, point) { | |
| point = point || floor.selected; | |
| // already exists | |
| if (null != blocks[point.x][point.y][point.z]) return false; | |
| // if a cube with the given color does not exist, create it | |
| if (null == cubes[color]) | |
| cubes[color] = createCube(BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE, color); | |
| var block = point.clone(); | |
| block.color = color; | |
| blocks[block.x][block.y][block.z] = block; | |
| scene.draw(); | |
| changed = true; | |
| if (changedHandler) changedHandler(); | |
| return true; | |
| }; | |
| /** | |
| * | |
| * @param {obelisk.Point3D} point | |
| * @returns {boolean} | |
| */ | |
| scene.remove = function(point) { | |
| point = point || floor.selected; | |
| if (null == blocks[point.x][point.y][point.z]) return false; | |
| blocks[point.x][point.y][point.z] = null; | |
| scene.draw(); | |
| changed = true; | |
| if (changedHandler) changedHandler(); | |
| return true; | |
| }; | |
| /** | |
| * | |
| * @param {number} delta | |
| */ | |
| scene.adjustFloor = function(delta) { | |
| floor.offset += delta; | |
| if (floor.offset < 0) floor.offset = 0; | |
| else if (floor.offset >= SIZE) floor.offset = SIZE - 1; | |
| scene.draw(); | |
| }; | |
| /** | |
| * | |
| * @param {number} direction | |
| */ | |
| scene.rotate = function(direction) { | |
| var b = blocks, | |
| n = SIZE, | |
| x, y, z, start, end; | |
| // transpose | |
| for (z = 0; z < n; z++) | |
| for (x = 0; x < n; x++) | |
| for (y = 0; y < n; y++) | |
| if (x < y) swap(b, x, y, y, x, z); | |
| if (1 === direction) { | |
| // reverse rows | |
| for (z = 0; z < n; z++) | |
| for (x = 0; x < n; x++) | |
| for (start = 0, end = n - 1; start < end; start++, end--) | |
| swap(b, x, start, x, end, z); | |
| } | |
| else if (-1 == direction) { | |
| // reverse cols | |
| for (z = 0; z < n; z++) | |
| for (y = 0; y < n; y++) | |
| for (start = 0, end = n - 1; start < end; start++, end--) | |
| swap(b, start, y, end, y, z); | |
| } | |
| scene.draw(); | |
| }; | |
| /** | |
| * | |
| * @param {boolean} silent | |
| * @returns {object} | |
| */ | |
| scene.save = function(silent) { | |
| var data = []; | |
| // creates a color table | |
| var colors = [], colorsHash = {}; | |
| for (var p in cubes) { | |
| if (!cubes.hasOwnProperty(p)) continue; | |
| colorsHash[p] = colors.length; | |
| colors.push(p); | |
| } | |
| // basically "compact" the memory structure by only saving existing blocks, | |
| // referencing colors in color table, with short properties name | |
| for (var x = 0; x < SIZE; x++) { | |
| for (var y = 0; y < SIZE; y++) { | |
| for (var z = 0; z < SIZE; z++) { | |
| if (null != blocks[x][y][z]) { | |
| var b = blocks[x][y][z]; | |
| data.push({ | |
| x: b.x, | |
| y: b.y, | |
| z: b.z, | |
| c: colorsHash[b.color] | |
| }); | |
| } | |
| } | |
| } | |
| } | |
| if (!silent) changed = false; | |
| return { | |
| colors: colors, | |
| data: data | |
| }; | |
| }; | |
| /** | |
| * | |
| * @param {object} data | |
| */ | |
| scene.load = function( data, colorFormat ) { | |
| var i; | |
| // ensure colors are numbers | |
| var colors = data.colors; | |
| for (i = 0; i < colors.length; i++) | |
| colors[i] = parseInt(colors[i]); | |
| data = data.data; | |
| blocks = matrix(SIZE); | |
| function componentToHex(c) { | |
| var hex = c.toString(16); | |
| return hex.length == 1 ? "0" + hex : hex; | |
| } | |
| function rgbToHex(r, g, b) { | |
| return "0x" + componentToHex(r) + componentToHex(g) + componentToHex(b); | |
| } | |
| var currentColor; | |
| for (i = 0; i < data.length; i++){ | |
| currentColor = ( colorFormat === "rgba" ) ? parseInt( rgbToHex( data[i].rgba[0], data[i].rgba[1], data[i].rgba[2] ) ) : colors[data[i].c]; | |
| scene.add( currentColor, new obelisk.Point3D(data[i].x, data[i].y, data[i].z) ); | |
| } | |
| scene.draw(); | |
| changed = false; | |
| }; | |
| /** | |
| * | |
| * @returns {string} | |
| */ | |
| scene.snapshot = function() { | |
| // isolate art | |
| view.clear(); | |
| view.context.fillStyle = '#222222'; | |
| view.context.fillRect(0, 0, canvasEl.width, canvasEl.height); | |
| drawBlocks(view, blocks, cubes, 0, SIZE); | |
| var image = canvasEl.toDataURL("image/png"); | |
| scene.draw(); | |
| return image; | |
| }; | |
| /** | |
| * | |
| */ | |
| scene.clear = function() { | |
| blocks = matrix(SIZE); | |
| scene.draw(); | |
| }; | |
| /** | |
| * | |
| * @param {function} callback | |
| * @returns {boolean|undefined} | |
| */ | |
| scene.changed = function(callback) { | |
| if (!callback) return changed; | |
| changedHandler = callback; | |
| return this; | |
| }; | |
| /** | |
| * | |
| * @param {number} width | |
| * @param {number} height | |
| * @param {string} hexColor | |
| * @returns {obelisk.Brick} | |
| * @private | |
| */ | |
| function createBrick(width, height, hexColor) { | |
| var dimension = new obelisk.BrickDimension(width, height); | |
| var color = new obelisk.SideColor().getByInnerColor(hexColor); | |
| return new obelisk.Brick(dimension, color, false); | |
| } | |
| /** | |
| * | |
| * @param {number} width | |
| * @param {number} height | |
| * @param {number} depth | |
| * @param {string} hexColor | |
| * @returns {obelisk.Cube} | |
| * @private | |
| */ | |
| function createCube(width, height, depth, hexColor) { | |
| var dimension = new obelisk.CubeDimension(width, height, depth); | |
| var color = new obelisk.CubeColor( | |
| obelisk.ColorGeom.applyBrightness(hexColor, -20 * 4), | |
| obelisk.ColorGeom.applyBrightness(hexColor, 60), | |
| obelisk.ColorGeom.applyBrightness(hexColor, -20 * 2), | |
| obelisk.ColorGeom.applyBrightness(hexColor, -20), | |
| hexColor | |
| ); | |
| return new obelisk.Cube(dimension, color); | |
| } | |
| /** | |
| * | |
| * @param {obelisk.Point3D} point | |
| * @private | |
| */ | |
| function clampBounds(point) { | |
| if (point.x < 0) point.x = 0; | |
| else if (point.x >= SIZE) point.x = SIZE - 1; | |
| if (point.y < 0) point.y = 0; | |
| else if (point.y >= SIZE) point.y = SIZE - 1; | |
| if (point.z < 0) point.z = 0; | |
| else if (point.z >= SIZE) point.z = SIZE - 1; | |
| } | |
| /** | |
| * | |
| * @param {number} size | |
| * @returns {array} | |
| * @private | |
| */ | |
| function matrix(size) { | |
| var m = new Array(size); | |
| for (var x = 0; x < size; x++) { | |
| m[x] = new Array(size); | |
| for (var y = 0; y < size; y++) { | |
| m[x][y] = new Array(size); | |
| } | |
| } | |
| return m; | |
| } | |
| /** | |
| * | |
| * @param {number} m | |
| * @param {number} x1 | |
| * @param {number} y1 | |
| * @param {number} x2 | |
| * @param {number} y2 | |
| * @param {number} z | |
| * @private | |
| */ | |
| function swap(m, x1, y1, x2, y2, z) { | |
| var tmp = m[x2][y2][z], c; | |
| c = m[x2][y2][z] = m[x1][y1][z]; | |
| if (c) { | |
| c.x = x2; | |
| c.y = y2; | |
| } | |
| c = m[x1][y1][z] = tmp; | |
| if (c) { | |
| c.x = x1; | |
| c.y = y1; | |
| } | |
| } | |
| /** | |
| * | |
| * @param {obelisk.PixelView} view | |
| * @param {object} floor | |
| * @private | |
| */ | |
| function drawFloor(view, floor) { | |
| var normal = floor.normal, | |
| highlighted = floor.highlighted, | |
| point = new obelisk.Point3D(); | |
| if (floor.offset > 0) { | |
| normal = floor.elevated; | |
| highlighted = floor.elevatedHighlighted; | |
| view.context.globalAlpha = 0.7; | |
| } | |
| for (var x = 0; x < 20; x++) { | |
| for (var y = 0; y < 20; y++) { | |
| if (x == floor.selected.x && y == floor.selected.y) continue; | |
| point.x = x * BLOCK_SIZE; | |
| point.y = y * BLOCK_SIZE; | |
| point.z = floor.offset * BLOCK_SIZE; | |
| view.renderObject(normal, point); | |
| } | |
| } | |
| if (-1 != floor.selected.x) { | |
| point.x = floor.selected.x * BLOCK_SIZE; | |
| point.y = floor.selected.y * BLOCK_SIZE; | |
| point.z = floor.offset * BLOCK_SIZE; | |
| view.renderObject(highlighted, point); | |
| } | |
| view.context.globalAlpha = 1; | |
| } | |
| /** | |
| * | |
| * @param {obelisk.PixelView} view | |
| * @param {array} blocks | |
| * @param {object} cubes | |
| * @param {number} startZ | |
| * @param {number} endZ | |
| * @private | |
| */ | |
| function drawBlocks(view, blocks, cubes, startZ, endZ) { | |
| var point = new obelisk.Point3D(); | |
| for (var x = 0; x < SIZE; x++) { | |
| for (var y = 0; y < SIZE; y++) { | |
| for (var z = startZ; z < endZ; z++) { | |
| var block = blocks[x][y][z]; | |
| if (!block) continue; | |
| point.x = block.x * BLOCK_SIZE; | |
| point.y = block.y * BLOCK_SIZE; | |
| point.z = block.z * BLOCK_SIZE; | |
| view.renderObject(cubes[block.color], point); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Exports. | |
| */ | |
| module.exports = scene; | |
| },{}],15:[function(require,module,exports){ | |
| /*! | |
| * obelisk-builder | |
| * Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com> | |
| * MIT Licensed | |
| */ | |
| 'use strict'; | |
| /** | |
| * Private variables | |
| */ | |
| var el = document.querySelector('#welcome'); | |
| var overlayEl = document.getElementById('overlay'); | |
| var onHideCallback; | |
| /** | |
| * Module declaration. | |
| */ | |
| var welcome = {}; | |
| /** | |
| * | |
| */ | |
| welcome.init = function(callback) { | |
| if (localStorage.getItem('visited')) return callback(); | |
| window.addEventListener('load', function() { | |
| setTimeout(show, 500); | |
| }); | |
| onHideCallback = callback; | |
| }; | |
| /** | |
| * @private | |
| */ | |
| function show() { | |
| toggle(); | |
| document.addEventListener('keypress', hide); | |
| localStorage.setItem('visited', true); | |
| } | |
| /** | |
| * @private | |
| */ | |
| function hide() { | |
| document.removeEventListener('keypress', hide); | |
| toggle(); | |
| onHideCallback(); | |
| } | |
| /** | |
| * @private | |
| */ | |
| function toggle() { | |
| el.classList.toggle('is-hidden'); | |
| overlayEl.classList.toggle('is-hidden'); | |
| } | |
| /** | |
| * Exports. | |
| */ | |
| module.exports = welcome; | |
| },{}]},{},[1]); | |
| app.init(); |
| @font-face { | |
| font-family: Lato; | |
| src: url('https://github.com/ngryman/obelisk-buildr/raw/master/fonts/lato.woff?92048847') format('woff'), | |
| url('https://github.com/ngryman/obelisk-buildr/raw/master/fonts/lato.ttf?92048847') format('truetype'); | |
| font-weight: normal; | |
| font-style: normal; | |
| } | |
| #auth button { | |
| display: inline-block; | |
| vertical-align: middle; | |
| margin: 0; | |
| padding: 8px; | |
| color: #eee; | |
| background-color: transparent; | |
| cursor: pointer; | |
| } | |
| #auth button.is-disabled, | |
| #auth button:active { | |
| color: #999; | |
| } | |
| #auth button > span { | |
| display: inline-block; | |
| vertical-align: middle; | |
| } | |
| #auth button .icon { | |
| margin-left: 10px; | |
| font-size: 25px; | |
| width: 25px; | |
| } | |
| #auth button.login { | |
| border: 1px solid #444; | |
| background: #000; | |
| } | |
| #auth button.login .icon { | |
| text-shadow: 0 0 2px #eee; | |
| } | |
| #auth button.logged { | |
| color: #00B2B2; | |
| } | |
| #auth button.logged .icon { | |
| -webkit-animation: glow 4s linear infinite; | |
| animation: glow 4s linear infinite; | |
| } | |
| #auth button.is-hidden { | |
| display: none; | |
| } | |
| @-webkit-keyframes glow { | |
| 50% { | |
| text-shadow: 0 0 20px #00B2B2; | |
| } | |
| } | |
| @keyframes glow { | |
| 50% { | |
| text-shadow: 0 0 20px #00B2B2; | |
| } | |
| } | |
| /** position fixed on small screens when they are in landscape mode */ | |
| @media (orientation: landscape) and (max-height: 400px) { | |
| #auth { | |
| position: fixed; | |
| top: 0; | |
| right: 0; | |
| } | |
| } | |
| * { | |
| -moz-box-sizing: border-box; | |
| box-sizing: border-box; | |
| } | |
| a, | |
| a:active | |
| a:visited { | |
| color: #eee; | |
| } | |
| a:active { | |
| text-decoration: none; | |
| } | |
| button { | |
| margin: 2px; | |
| padding: 0; | |
| border: none; | |
| outline: none; | |
| } | |
| html, body, main { | |
| margin: 0; | |
| width: 100%; | |
| height: 100%; | |
| overflow: hidden; | |
| } | |
| body { | |
| min-width: 320px; | |
| background-color: #111; | |
| color: #eee; | |
| font-family: Lato, Arial, sans-serif; | |
| -webkit-user-select: none; | |
| -moz-user-select: none; | |
| -ms-user-select: none; | |
| user-select: none; | |
| } | |
| main { | |
| text-align: center; | |
| } | |
| @media (min-width: 700px) and (min-height: 700px), (min-height: 700px) { | |
| main { | |
| padding-top: 70px; | |
| padding-bottom: 70px; | |
| } | |
| } | |
| #buildr { | |
| height: 95%; /* Stretching a little... */margin-top: -30px; | |
| } | |
| #overlay { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: #000; | |
| opacity: .3; | |
| transition: opacity 200ms; | |
| -webkit-backface-visibility: hidden; | |
| backface-visibility: hidden; | |
| } | |
| #overlay.is-hidden { | |
| opacity: 0; | |
| pointer-events: none; | |
| } | |
| footer { | |
| display: none; | |
| } | |
| @media (min-width: 700px) and (min-height: 700px), (min-height: 700px) { | |
| footer { | |
| display: block; | |
| position: absolute; | |
| bottom: 15px; | |
| left: 0; | |
| padding: 10px; | |
| width: 100%; | |
| text-align: center; | |
| color: #999; | |
| } | |
| footer a { | |
| padding: 6px; | |
| color: #999; | |
| font-size: 14px; | |
| text-decoration: none; | |
| text-shadow: 0 0 2px black; | |
| border-radius: 4px; | |
| transition: all 200ms; | |
| } | |
| footer a:hover { | |
| background: black; | |
| } | |
| footer a strong, | |
| footer a em { | |
| font-style: normal; | |
| font-weight: normal; | |
| color: #eee; | |
| } | |
| footer a strong { | |
| transition: all 200ms 200ms; | |
| } | |
| footer a strong:hover { | |
| text-shadow: 0 0 1px #fff; | |
| } | |
| } | |
| header { | |
| display: block; | |
| position: absolute; | |
| z-index: 1; | |
| padding: 6px 15px; | |
| width: 100%; | |
| text-align: justify; | |
| } | |
| header::after { | |
| content: ''; | |
| display: inline-block; | |
| width: 100%; | |
| height: 0; | |
| font-size: 0; | |
| line-height: 0; | |
| } | |
| header > * { | |
| display: inline-block; | |
| vertical-align: middle; | |
| } | |
| header h1 { | |
| margin: 0; | |
| height: 54px; | |
| line-height: 54px; | |
| color: #fff; | |
| text-align: left; | |
| font-weight: 300; | |
| } | |
| header h1 span { | |
| color: #00B2B2; | |
| } | |
| header h1:before { | |
| content: ''; | |
| display: inline-block; | |
| vertical-align: text-top; | |
| margin-right: 15px; | |
| width: 40px; | |
| height: 40px; | |
| background-image: url(http://ngryman.sh/obelisk-buildr/logo.png); | |
| background-position: left center; | |
| background-repeat: no-repeat; | |
| background-size: 40px; | |
| -webkit-animation: doodle 4s linear infinite; | |
| animation: doodle 4s linear infinite; | |
| } | |
| @-webkit-keyframes doodle { | |
| 25% { -webkit-transform: rotate(5deg); transform: rotate(5deg); } | |
| 75% { -webkit-transform: rotate(-5deg); transform: rotate(-5deg); } | |
| } | |
| @keyframes doodle { | |
| 25% { -webkit-transform: rotate(5deg); transform: rotate(5deg); } | |
| 75% { -webkit-transform: rotate(-5deg); transform: rotate(-5deg); } | |
| } | |
| /** | |
| * hide on small screens when they are in landscape mode | |
| * | |
| * [1]: using top instead of transform, because we can't have | |
| * position fixed elements in transformed elements. This | |
| * is needed for github auth buttons on small screens. | |
| */ | |
| @media (orientation: landscape) and (max-height: 400px) { | |
| header { | |
| top: -70px; /* [1] */ | |
| } | |
| } | |
| #help { | |
| top: 25%; | |
| left: 0; | |
| width: 300px; | |
| } | |
| #help.is-hidden { | |
| opacity: 0; | |
| -webkit-transform: translateX(-100%) scale(.8); | |
| transform: translateX(-100%) scale(.8); | |
| } | |
| .icon { | |
| display: inline-block; | |
| font-style: normal; | |
| font-weight: normal; | |
| speak: none; | |
| text-decoration: inherit; | |
| width: 1em; | |
| margin-right: .2em; | |
| text-align: center; | |
| } | |
| .icon-git:before { content: '\e801'; } | |
| .icon-github:before { content: '\e800'; } | |
| /** | |
| * [1]: fixes blurry text on Chrome because of transform... | |
| */ | |
| #notification { | |
| position: absolute; | |
| top: 0; | |
| left: 50%; | |
| z-index: 1; | |
| width: 100%; | |
| max-width: 400px; | |
| padding: 5px 40px; | |
| color: #fff; | |
| font-size: 16px; /* [1] */ | |
| -webkit-transform: translateX(-50%); /* [1] */ | |
| transform: translateX(-50%); | |
| transition: -webkit-transform 300ms; | |
| transition: transform 300ms; | |
| } | |
| #notification.is-hidden { | |
| -webkit-transform: translate(-50%, -200%); | |
| transform: translate(-50%, -200%); | |
| } | |
| #notification a, | |
| #notification a:active | |
| #notification a:visited { | |
| color: #fff; | |
| } | |
| #notification[data-type=info] { | |
| background-color: #00B2B2; | |
| } | |
| #notification[data-type=error] { | |
| background-color: #c82829; | |
| } | |
| #palette { | |
| position: fixed; | |
| z-index: 1; | |
| padding: 5px; | |
| width: 200px; | |
| background: rgba(17, 17, 17, .9); | |
| opacity: 1; | |
| text-align: left; | |
| -webkit-transform: scale(1); | |
| transform: scale(1); | |
| /*transform: translateX(-50%) translateY(-50%);*/ | |
| -webkit-backface-visibility: hidden; | |
| /*transform: translateX(-50%) translateY(-50%);*/ | |
| backface-visibility: hidden; | |
| border: 1px solid #444; | |
| box-shadow: 0 0 0 1px #000, 0 0 15px rgba(0, 0, 0, .5); | |
| transition: opacity 100ms, -webkit-transform 100ms; | |
| transition: opacity 100ms, transform 100ms; | |
| } | |
| #palette.is-hidden { | |
| opacity: 0; | |
| -webkit-transform: scale(.7); | |
| transform: scale(.7); | |
| pointer-events: none; | |
| } | |
| #palette button { | |
| width: 30px; | |
| height: 30px; | |
| font-size: 10px; | |
| } | |
| #palette button.is-active { | |
| box-shadow: 0 0 0 1px rgba(255, 255, 255, .4) inset; | |
| } | |
| #palette .red { | |
| background: linear-gradient(#c82829, rgba(200, 40, 41, 0.80)); | |
| } | |
| #palette .red:active { | |
| background: linear-gradient(#af2829, rgba(175, 40, 41, 0.80)); | |
| } | |
| #palette .orange { | |
| background: linear-gradient(#f5871f, rgba(245, 135, 31, 0.80)); | |
| } | |
| #palette .orange:active { | |
| background: linear-gradient(#c8651f, rgba(200, 104, 31, 0.80)); | |
| } | |
| #palette .yellow { | |
| background: linear-gradient(#eab700, rgba(234, 183, 0, 0.80)); | |
| } | |
| #palette .yellow:active { | |
| background: linear-gradient(#bd8b00, rgba(189, 147, 0, 0.80)); | |
| } | |
| #palette .green { | |
| background: linear-gradient(#718c00, rgba(113, 140, 0, 0.80)); | |
| } | |
| #palette .green:active { | |
| background: linear-gradient(#475f00, rgba(71, 95, 0, 0.80)); | |
| } | |
| #palette .aqua { | |
| background: linear-gradient(#3e999f, rgba(62, 153, 159, 0.81)); | |
| } | |
| #palette .aqua:active { | |
| background: linear-gradient(#396c72, rgba(53, 108, 114, 0.81)); | |
| } | |
| #palette .blue { | |
| background: linear-gradient(#4271ae, rgba(66, 113, 174, 0.81)); | |
| } | |
| #palette .blue:active { | |
| background: linear-gradient(#404681, rgba(62, 71, 129, 0.81)); | |
| } | |
| #palette .purple { | |
| background: linear-gradient(#8959a8, rgba(137, 89, 168, 0.80)); | |
| } | |
| #palette .purple:active { | |
| background: linear-gradient(#5c3b7b, rgba(93, 59, 123, 0.80)); | |
| } | |
| #palette .black { | |
| background: linear-gradient(#4d4d4c, rgba(77, 77, 76, 0.80)); | |
| } | |
| #palette .black:active { | |
| background: linear-gradient(#20201f, rgba(32, 32, 31, 0.80)); | |
| } | |
| #palette .white { | |
| background: linear-gradient(#ddd, rgba(221, 221, 221, 0.80)); | |
| } | |
| #palette .white:active { | |
| background: linear-gradient(#b0b0b0, rgba(176, 176, 176, 0.80)); | |
| } | |
| .pane { | |
| position: fixed; | |
| z-index: 1; | |
| padding: 15px 30px; | |
| background: rgba(17, 17, 17, .9); | |
| border: 1px solid #444; | |
| box-shadow: 0 0 0 1px #000, 0 0 15px rgba(0, 0, 0, .5); | |
| transition: all 300ms; | |
| border-radius: 2px; | |
| } | |
| .pane h2 { | |
| font-weight: 300; | |
| } | |
| .pane ul { | |
| margin-top: 20px; | |
| padding-left: 20px; | |
| } | |
| .pane p { | |
| text-align: left; | |
| } | |
| .pane b { | |
| margin: 0 2px; | |
| padding: 0 2px; | |
| font-weight: 300; | |
| color: #000; | |
| background: #fff; | |
| } | |
| #scene { | |
| width: 100%; | |
| height: 100%; | |
| -webkit-backface-visibility: hidden; | |
| backface-visibility: hidden; | |
| } | |
| @media (min-width: 800px) { | |
| #scene { | |
| width: 800px; | |
| } | |
| } | |
| #welcome { | |
| top: 70px; | |
| width: 100%; | |
| -webkit-transform: scale(1) rotate(0deg); | |
| transform: scale(1) rotate(0deg); | |
| transition: all 500ms; | |
| } | |
| @media (min-width: 500px) and (min-height: 600px) { | |
| /** | |
| * [1]: fixes blurry text on Chrome because of transform -50%... | |
| */ | |
| #welcome { | |
| left: 50%; | |
| margin-left: -250px; /* [1] */ | |
| width: 500px; | |
| } | |
| } | |
| @media (min-height: 700px) { | |
| #welcome { | |
| top: 25%; | |
| } | |
| } | |
| #welcome.is-hidden { | |
| opacity: 0; | |
| -webkit-transform: scale(0) rotate(720deg); | |
| transform: scale(0) rotate(720deg); | |
| } | |
| .thumbnails { | |
| list-style: none; | |
| margin: 10px 0 8px 0; | |
| display: inline-block; | |
| } | |
| .thumbnails li { | |
| display: inline; | |
| margin: 10px; | |
| } | |
| .thumbnails li.active { | |
| border-top: 10px solid rgb(70, 157, 70); | |
| } | |
| .currently { | |
| margin: 0; | |
| padding: 0 0 0 0; | |
| display: inline-block; | |
| font-size: 18px; | |
| margin-right: 25px; | |
| } | |
| .select { | |
| font-size: 18px; | |
| margin: 0; | |
| } | |
| .drop-image { | |
| display: none; | |
| } | |
| .drop-zone { | |
| display: inline-block; | |
| height: 20px; | |
| width: 20px; | |
| border: 2px dashed #666; | |
| margin-left: 10px; | |
| margin-right: 10px; | |
| } | |
| .drop-over { | |
| border-color: #CACACA; | |
| background-color: #2d2d2d; | |
| } |