├── favicon.ico ├── favicon.png ├── img ├── dabblet.ttf ├── loader.png ├── noise.png ├── scrob.png ├── scrob_1.png ├── scrob_10.png ├── scrob_100.png ├── scrob_50.png ├── scrob_50.psd └── settings.png ├── result ├── .htaccess └── index.php ├── result.html ├── .htaccess ├── scrubbingValues.css ├── sample.oauth.php ├── code ├── util.js ├── selection.js ├── incrementable.js ├── scrubbingValues.js ├── prefixfree.min.js ├── previewers.js ├── utopia.js ├── code-highlight.js ├── editor.js └── dabblet.js ├── help ├── style.css └── index.html ├── index.html └── style.css /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altryne/dabblet/master/favicon.ico -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altryne/dabblet/master/favicon.png -------------------------------------------------------------------------------- /img/dabblet.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altryne/dabblet/master/img/dabblet.ttf -------------------------------------------------------------------------------- /img/loader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altryne/dabblet/master/img/loader.png -------------------------------------------------------------------------------- /img/noise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altryne/dabblet/master/img/noise.png -------------------------------------------------------------------------------- /img/scrob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altryne/dabblet/master/img/scrob.png -------------------------------------------------------------------------------- /img/scrob_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altryne/dabblet/master/img/scrob_1.png -------------------------------------------------------------------------------- /img/scrob_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altryne/dabblet/master/img/scrob_10.png -------------------------------------------------------------------------------- /img/scrob_100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altryne/dabblet/master/img/scrob_100.png -------------------------------------------------------------------------------- /img/scrob_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altryne/dabblet/master/img/scrob_50.png -------------------------------------------------------------------------------- /img/scrob_50.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altryne/dabblet/master/img/scrob_50.psd -------------------------------------------------------------------------------- /img/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altryne/dabblet/master/img/settings.png -------------------------------------------------------------------------------- /result/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | 3 | RewriteBase / 4 | RewriteCond %{REQUEST_FILENAME} !-f 5 | RewriteCond %{REQUEST_FILENAME} !-d 6 | RewriteRule . /result/index.php [L] -------------------------------------------------------------------------------- /result.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Dabblet result 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | 3 | RewriteBase / 4 | RewriteCond %{REQUEST_FILENAME} !-f 5 | RewriteCond %{REQUEST_FILENAME} !-d 6 | RewriteRule . /index.html [L] 7 | 8 | # Compress code 9 | AddOutputFilterByType DEFLATE text/plain text/html text/xml text/css text/javascript image/svg+xml application/xml application/xhtml+xml application/rss+xml application/javascript application/x-javascript -------------------------------------------------------------------------------- /scrubbingValues.css: -------------------------------------------------------------------------------- 1 | body[data-ctrlmode = "true"] { 2 | 3 | /*background:green;*/ 4 | 5 | } 6 | body[data-ctrlmode = "true"] #css-container{ 7 | user-select:none; 8 | } 9 | body[data-ctrlmode = "true"] .abslength{ 10 | cursor: url(img/scrob.png),ew-resize; 11 | } 12 | 13 | /* 14 | trying to change the cusror as the user drags - impossible on chrome mac 15 | */ 16 | body[data-scrubbing] *{ 17 | cursor: url(img/scrob.png),ew-resize !important; 18 | } 19 | body[data-scrubbing = "0.1"] *{ 20 | cursor: url(img/scrob_1.png),ew-resize !important; 21 | } 22 | body[data-scrubbing = "10"] *{ 23 | cursor: url(img/scrob_10.png),ew-resize !important; 24 | } 25 | 26 | body[data-scrubbing = "50"] *{ 27 | cursor: url(img/scrob_50.png),ew-resize !important; 28 | } 29 | body[data-scrubbing = "100"] *{ 30 | cursor: url(img/scrob_100.png),ew-resize !important; 31 | } 32 | -------------------------------------------------------------------------------- /sample.oauth.php: -------------------------------------------------------------------------------- 1 | 31 | -------------------------------------------------------------------------------- /result/index.php: -------------------------------------------------------------------------------- 1 | 42 | 43 | 44 | 45 | 46 | <?= $data['description'] ?> 47 | 50 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /code/util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Helper functions 3 | */ 4 | String.prototype.splice = function(i, remove, add) { 5 | remove = +remove || 0; 6 | add = add || ''; 7 | 8 | return this.slice(0,i) + add + this.slice(i + remove); 9 | }; 10 | 11 | function offset(element) { 12 | var left = 0, top = 0, el = element; 13 | 14 | if (el.parentNode) { 15 | do { 16 | left += el.offsetLeft - el.scrollLeft; 17 | top += el.offsetTop - el.scrollTop; 18 | } while ((el = el.parentNode) && el.nodeType < 9); 19 | } 20 | 21 | return { 22 | top: top, 23 | right: innerWidth - left - element.offsetWidth, 24 | bottom: innerHeight - top - element.offsetHeight, 25 | left: left, 26 | }; 27 | } 28 | 29 | function xhr(o) { 30 | document.body.setAttribute('data-loading', ''); 31 | 32 | var xhr = new XMLHttpRequest(), 33 | method = o.method || 'GET', 34 | data = o.data || ''; 35 | 36 | xhr.open(method, o.url + (method === 'GET' && data? '?' + data : ''), true); 37 | 38 | if(method !== 'GET') { 39 | xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 40 | } 41 | 42 | if(o.headers) { 43 | for(var header in o.headers) { 44 | xhr.setRequestHeader(header, o.headers[header]); 45 | } 46 | } 47 | 48 | xhr.onreadystatechange = function(){ 49 | if(xhr.readyState === 4) { 50 | document.body.removeAttribute('data-loading'); 51 | 52 | if(xhr.responseText) { 53 | o.callback(xhr); 54 | } 55 | } 56 | }; 57 | 58 | xhr.send(method === 'GET'? null : data); 59 | 60 | return xhr; 61 | } 62 | 63 | function script(url, callback, doc) { 64 | doc = doc || document; 65 | 66 | var script = doc.createElement('script'); 67 | script.src = url; 68 | script.async = true; 69 | doc.documentElement.appendChild(script); 70 | 71 | script.onload = callback; 72 | } 73 | 74 | -------------------------------------------------------------------------------- /code/selection.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | Object.defineProperty(HTMLPreElement.prototype, 'selectionStart', { 4 | get: function() { 5 | var selection = getSelection(); 6 | 7 | if(selection.rangeCount) { 8 | var range = selection.getRangeAt(0), 9 | element = range.startContainer, 10 | container = element, 11 | offset = range.startOffset; 12 | 13 | if(!(this.compareDocumentPosition(element) & 0x10)) { 14 | return 0; 15 | } 16 | 17 | do { 18 | while(element = element.previousSibling) { 19 | if(element.textContent) { 20 | offset += element.textContent.length; 21 | } 22 | } 23 | 24 | element = container = container.parentNode; 25 | } while(element && element != this); 26 | 27 | return offset; 28 | } 29 | else { 30 | return 0; 31 | } 32 | }, 33 | 34 | enumerable: true, 35 | configurable: true 36 | }); 37 | 38 | Object.defineProperty(HTMLPreElement.prototype, 'selectionEnd', { 39 | get: function() { 40 | var selection = getSelection(); 41 | 42 | if(selection.rangeCount) { 43 | return this.selectionStart + (selection.getRangeAt(0) + '').length; 44 | } 45 | else { 46 | return 0; 47 | } 48 | }, 49 | 50 | enumerable: true, 51 | configurable: true 52 | }); 53 | 54 | HTMLPreElement.prototype.setSelectionRange = function(ss, se) { 55 | var range = document.createRange(), 56 | offset = findOffset(this, ss); 57 | 58 | range.setStart(offset.element, offset.offset); 59 | 60 | if(se && se != ss) { 61 | offset = findOffset(this, se); 62 | } 63 | 64 | range.setEnd(offset.element, offset.offset); 65 | 66 | var selection = window.getSelection(); 67 | selection.removeAllRanges(); 68 | selection.addRange(range); 69 | } 70 | 71 | function findOffset(root, ss) { 72 | if(!root) { 73 | return null; 74 | } 75 | 76 | var offset = 0, 77 | element = root; 78 | 79 | do { 80 | var container = element; 81 | element = element.firstChild; 82 | 83 | if(element) { 84 | do { 85 | var len = element.textContent.length; 86 | 87 | if(offset <= ss && offset + len > ss) { 88 | break; 89 | } 90 | 91 | offset += len; 92 | } while(element = element.nextSibling); 93 | } 94 | 95 | if(!element) { 96 | // It's the container's lastChild 97 | break; 98 | } 99 | } while(element && element.hasChildNodes() && element.nodeType != 3); 100 | 101 | if(element) { 102 | return { 103 | element: element, 104 | offset: ss - offset 105 | }; 106 | } 107 | else if(container) { 108 | element = container; 109 | 110 | while(element && element.lastChild) { 111 | element = element.lastChild; 112 | } 113 | 114 | if(element.nodeType === 3) { 115 | return { 116 | element: element, 117 | offset: element.textContent.length 118 | }; 119 | } 120 | else { 121 | return { 122 | element: element, 123 | offset: 0 124 | }; 125 | } 126 | } 127 | 128 | return { 129 | element: root, 130 | offset: 0, 131 | error: true 132 | }; 133 | } 134 | 135 | })(); -------------------------------------------------------------------------------- /help/style.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Variables 3 | */ 4 | 5 | @font-face { 6 | font-family: 'Dabblet'; 7 | src: url(../img/dabblet.ttf); 8 | } 9 | 10 | @font-face { 11 | font-family: Primary; 12 | src: local('Baskerville'); 13 | } 14 | 15 | 16 | /** 17 | * Styles 18 | */ 19 | * { 20 | margin: 0; 21 | } 22 | 23 | html { 24 | background: url(../img/noise.png) hsl(24, 20%, 95%); 25 | max-width: 40em; 26 | padding-left: 12em; 27 | margin: auto; 28 | } 29 | 30 | body { 31 | padding: 1em; 32 | border-left: 1px solid hsla(200,10%,20%,.4); 33 | margin: 2em auto; 34 | font: 110%/1.5 Primary; 35 | hyphens: auto; 36 | text-shadow: 0 0.0847em white; /* See issue #134 */ 37 | } 38 | 39 | h1, h2 { 40 | font: bold 200%/1 sans-serif; 41 | } 42 | 43 | body > hgroup { 44 | padding: .5em 0; 45 | color: black; 46 | text-align: center; 47 | font-size: 400%; 48 | line-height: .4; 49 | } 50 | 51 | body > hgroup > h1 { 52 | font-family: Dabblet, sans-serif; 53 | font-size: 100%; 54 | } 55 | 56 | body > hgroup > h2 { 57 | margin-top: -.7em; 58 | font: italic 80%/1 Primary; 59 | text-indent: 4em; 60 | color: #a34; 61 | 62 | } 63 | 64 | p { 65 | margin: 1em 0; 66 | } 67 | 68 | a { 69 | color: hsl(200, 10%, 20%); 70 | } 71 | 72 | kbd { 73 | display: inline-block; 74 | min-width: 1em; 75 | padding: .2em; 76 | border: 1px solid #ddd; 77 | border-radius: .3em; 78 | background: white; 79 | text-align: center; 80 | line-height: 1; 81 | } 82 | 83 | kbd[title] { 84 | cursor: help; 85 | } 86 | 87 | section:first-of-type, 88 | section#toc { 89 | font-size: 120%; 90 | } 91 | 92 | section h1, 93 | section h2 { 94 | margin: 1.5em 0 .5em; 95 | line-height: 1; 96 | letter-spacing: -.05em; 97 | } 98 | 99 | section h1 { 100 | font-size: 350%; 101 | } 102 | 103 | section h2 { 104 | font-size: 200%; 105 | color: hsl(200, 10%, 20%); 106 | } 107 | 108 | nav { 109 | position: fixed; 110 | top: 13em; 111 | width: 12em; 112 | margin-left: -14em; 113 | text-align: right; 114 | } 115 | 116 | nav:not(:hover) { 117 | color: hsla(200,10%,20%,.4); 118 | } 119 | 120 | nav a { 121 | text-decoration: none; 122 | color: inherit; 123 | } 124 | 125 | nav a:hover { 126 | color: #a34; 127 | } 128 | 129 | table { 130 | border-spacing: 0; 131 | } 132 | 133 | thead { 134 | background: rgba(0,0,0,.1); 135 | } 136 | 137 | td { 138 | border-bottom: 1px solid rgba(0,0,0,.3); 139 | padding: .3em .5em; 140 | vertical-align: top; 141 | } 142 | 143 | tbody td:first-child { 144 | width: 10em; 145 | } 146 | 147 | tbody td:first-child > kbd { 148 | font-size: 120%; 149 | } 150 | 151 | tbody td:first-child > .note { 152 | font-size: 65%; 153 | } 154 | 155 | footer { 156 | border-top: 1px solid rgba(0,0,0,.2); 157 | padding: .5em; 158 | text-align: center; 159 | } 160 | 161 | #quick-links { 162 | font-style: italic; 163 | } 164 | 165 | #quick-links > a { 166 | padding: .4em .8em; 167 | background: url('../img/noise.png') hsla(200, 10%, 20%,.6); 168 | color: white; 169 | font-weight: bold; 170 | border-radius: 999px; 171 | text-decoration: none; 172 | text-shadow: 0 -1px 1px black; 173 | box-shadow: 0 .1em white; 174 | } 175 | 176 | #quick-links > a:hover { 177 | background-color: hsl(200, 10%, 20%); 178 | } 179 | 180 | ::selection { 181 | background-color: initial; /* See issue #140 */ 182 | color: white; 183 | text-shadow: none; /* See issue #135 */ 184 | } 185 | -------------------------------------------------------------------------------- /code/incrementable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Script for making multiple numbers in a textfield incrementable/decrementable (like Firebug's CSS values) 3 | * @author Lea Verou 4 | * @version 1.1 5 | */ 6 | 7 | (function(){ 8 | 9 | /** 10 | * Constructor 11 | * @param textField {HTMLElement} An input or textarea element 12 | * @param multiplier {Function} A function that accepts the event object and returns the multiplier or 0 for nothing to happen. 13 | */ 14 | var _ = window.Incrementable = function(textField, multiplier, units) { 15 | var me = this; 16 | 17 | this.textField = textField; 18 | 19 | this.step = +textField.getAttribute('step') || 20 | +textField.getAttribute('data-step') || 1; 21 | 22 | this.multiplier = multiplier || function(evt) { 23 | if(evt.shiftKey) { return 10; } 24 | 25 | if(evt.ctrlKey) { return .1; } 26 | 27 | return 1; 28 | }; 29 | 30 | if(units) { 31 | this.units = units; 32 | } 33 | 34 | this.changed = false; 35 | 36 | this.textField.addEventListener('keydown', function(evt) { 37 | var multiplier = me.multiplier(evt); 38 | 39 | if(multiplier && (evt.keyCode == 38 || evt.keyCode == 40)) { 40 | me.changed = false; 41 | 42 | // Up or down arrow pressed, check if there's something 43 | // increment/decrement-able under the caret 44 | var caret = this.selectionStart, text = this.value, 45 | regex = new RegExp('^([\\s\\S]{0,' + caret + '}[^-0-9\\.])(-?[0-9]*(?:\\.?[0-9]+)(?:' + me.units + '))\\b', 'i'), 46 | property = 'value' in this? 'value' : 'textContent'; 47 | 48 | this[property] = this[property].replace(regex, function($0, $1, $2) { 49 | if($1.length <= caret && $1.length + $2.length >= caret) { 50 | me.changed = true; 51 | return $1 + me.stepValue($2, evt.keyCode == 40, multiplier); 52 | } 53 | else { 54 | return $1 + $2; 55 | } 56 | }); 57 | 58 | if(me.changed) { 59 | this.setSelectionRange(caret, caret); 60 | 61 | evt.preventDefault(); 62 | evt.stopPropagation(); 63 | } 64 | } 65 | }, false); 66 | 67 | this.textField.addEventListener('keypress', function(evt) { 68 | if(me.changed && me.checkModifiers(evt) 69 | && (evt.keyCode == 38 || evt.keyCode == 40)) 70 | evt.preventDefault(); 71 | evt.stopPropagation(); 72 | me.changed = false; 73 | }, false); 74 | } 75 | 76 | _.prototype = { 77 | /** 78 | * Gets a and increments or decrements it 79 | */ 80 | stepValue: function(length, decrement, multiplier) { 81 | var val = parseFloat(length), 82 | offset = (decrement? -1 : 1) * (multiplier || 1) * this.step, 83 | valPrecision = precision(val), 84 | offsetPrecision = precision(offset); 85 | 86 | // Prevent rounding errors 87 | var newVal = (parseFloat((val + offset).toPrecision( 88 | Math.max(valPrecision.integer, offsetPrecision.integer) + 89 | Math.max(valPrecision.decimals, offsetPrecision.decimals) 90 | ))); 91 | 92 | return newVal + length.replace(/^-|[0-9]+|\./g, ''); 93 | }, 94 | 95 | units: '|%|deg|px|r?em|ex|ch|in|cm|mm|pt|pc|vmin|vw|vh|gd|m?s' 96 | }; 97 | 98 | 99 | })(); 100 | 101 | function precision(number) { 102 | number = (number + '').replace(/^0+/, ''); 103 | 104 | var dot = number.indexOf('.'); 105 | 106 | if (dot === -1) { 107 | return { 108 | integer: number.length, 109 | decimals: 0 110 | }; 111 | } 112 | 113 | return { 114 | integer: dot, 115 | decimals: number.length - 1 - dot 116 | }; 117 | } 118 | -------------------------------------------------------------------------------- /code/scrubbingValues.js: -------------------------------------------------------------------------------- 1 | /** 2 | * User: altryne 3 | * Date: 17/2/12 4 | * Time: 20:31 5 | * inspired by bret victor's talk - http://vimeo.com/36579366 6 | */ 7 | 8 | var sv = scrubbingValues = { 9 | token : false, 10 | allowedTokens : ['abslength'], 11 | init : function(){ 12 | 13 | this.bindEvents(); 14 | }, 15 | bindEvents : function(){ 16 | 17 | document.addEventListener('keydown',function(e){ 18 | if(e.metaKey || e.ctrlKey){ 19 | sv.setCtrlMode(true); 20 | } 21 | },false); 22 | 23 | document.addEventListener('keyup',function(e){ 24 | if(!e.metaKey || !e.ctrlKey){ 25 | sv.setCtrlMode(false); 26 | } 27 | },false); 28 | 29 | window.addEventListener('mousedown',this.handleMouseDown,false); 30 | window.addEventListener('mouseup',this.handleMouseUp,false); 31 | window.addEventListener('mousemove',this.handleMouseMove,false); 32 | }, 33 | setCtrlMode : function(mode){ 34 | sv.ctrlMode = mode; 35 | $('body').dataset.ctrlmode = mode; 36 | 37 | }, 38 | handleMouseDown : function(e){ 39 | if(!sv.ctrlMode ) {return} 40 | //check if token is allowed to resize 41 | if(sv.allowedTokens.indexOf(Previewer.get(e.target)) > -1){ 42 | //set currently resizable token 43 | sv.token = e.target; 44 | //update previwer 45 | sv.updatePreviewer(); 46 | 47 | //handle abslength tokens 48 | var initialValue = parseInt(e.target.textContent); 49 | console.log(e.target.textContent); 50 | sv.token.dataset.value = initialValue; 51 | $('body').dataset.scrubbing = 1; 52 | /* add previewer to this token */ 53 | 54 | sv.screenX = e.screenX; 55 | sv.screenY = e.screenY; 56 | } 57 | }, 58 | handleMouseUp : function(e){ 59 | 60 | if(!sv.ctrlMode) {return} 61 | //clear previewer if mouseup outside element 62 | if(e.target !== sv.token && sv.token){ 63 | $('body').removeAttribute('data-scrubbing'); 64 | sv.removePreviewer(); 65 | } 66 | sv.token = false; 67 | }, 68 | handleMouseMove : function(e){ 69 | if(!sv.ctrlMode) {return} 70 | if(!sv.token) {return} 71 | //increment by distance from element - apple scrobbler style 72 | var increment = 1; 73 | 74 | 75 | if(sv.screenY - e.screenY > 15){ 76 | //15px above = 10px increment 77 | increment = 10; 78 | } 79 | if(sv.screenY - e.screenY > 30){ 80 | //50px above = 50px increment 81 | increment = 50; 82 | } 83 | if(sv.screenY - e.screenY > 150){ 84 | //150px above = 50px increment 85 | increment = 100; 86 | } 87 | if(sv.screenY - e.screenY < -15){ 88 | //15px below = .1px increment 89 | increment = 0.1; 90 | } 91 | $('body').dataset.scrubbing = increment; 92 | //handle stupid js math 93 | //x100 to prevent 0.00000018 values 94 | var val = parseFloat(sv.token.dataset.value); 95 | 96 | if(sv.screenX > e.screenX){ 97 | sv.token.dataset.value = Math.round((val * 100 - increment * 100)) / 100; 98 | } 99 | if(sv.screenX < e.screenX){ 100 | sv.token.dataset.value = Math.round((val * 100 + increment * 100)) / 100; 101 | } 102 | 103 | sv.updateTokenValue(); 104 | //save the last mouse position 105 | sv.screenX = e.screenX; 106 | }, 107 | updateTokenValue : function(){ 108 | if(!sv.ctrlMode && !sv.token) {return} 109 | 110 | var that = sv.token; 111 | 112 | var val = parseFloat(that.dataset.value); 113 | var unit = (that.textContent.match(/[a-z]+$/i) || [])[0]; 114 | var html = val + unit; 115 | that.textContent = html; 116 | //update previewer 117 | sv.updatePreviewer(); 118 | //update css 119 | cssCode = css.textContent; 120 | Dabblet.update.CSS(cssCode); 121 | }, 122 | updatePreviewer : function(token){ 123 | var token = token || sv.token; 124 | var type = Previewer.get(token); 125 | Previewer.s[type].token = token; 126 | }, 127 | removePreviewer : function(){ 128 | this.updatePreviewer(null); 129 | } 130 | } 131 | 132 | scrubbingValues.init(); 133 | -------------------------------------------------------------------------------- /code/prefixfree.min.js: -------------------------------------------------------------------------------- 1 | // StyleFix 1.0.1 & PrefixFree 1.0.4 / by Lea Verou / MIT license 2 | (function(){function h(a,b){return[].slice.call((b||document).querySelectorAll(a))}if(window.addEventListener){var b=window.StyleFix={link:function(a){try{if(!/\bstylesheet\b/i.test(a.rel)||!a.sheet.cssRules)return}catch(c){return}var d=a.href||a.getAttribute("data-href"),f=d.replace(/[^\/]+$/,""),g=a.parentNode,e=new XMLHttpRequest;e.open("GET",d);e.onreadystatechange=function(){if(4===e.readyState){var c=e.responseText;if(c&&a.parentNode){c=b.fix(c,!0,a);f&&(c=c.replace(/url\((?:'|")?(.+?)(?:'|")?\)/gi, 3 | function(a,c){return!/^([a-z]{3,10}:|\/|#)/i.test(c)?'url("'+f+c+'")':a}),c=c.replace(RegExp("\\b(behavior:\\s*?url\\('?\"?)"+f,"gi"),"$1"));var d=document.createElement("style");d.textContent=c;d.media=a.media;d.disabled=a.disabled;d.setAttribute("data-href",a.getAttribute("href"));g.insertBefore(d,a);g.removeChild(a)}}};e.send(null);a.setAttribute("data-inprogress","")},styleElement:function(a){var c=a.disabled;a.textContent=b.fix(a.textContent,!0,a);a.disabled=c},styleAttribute:function(a){var c= 4 | a.getAttribute("style"),c=b.fix(c,!1,a);a.setAttribute("style",c)},process:function(){h('link[rel~="stylesheet"]:not([data-inprogress])').forEach(StyleFix.link);h("style").forEach(StyleFix.styleElement);h("[style]").forEach(StyleFix.styleAttribute)},register:function(a,c){(b.fixers=b.fixers||[]).splice(void 0===c?b.fixers.length:c,0,a)},fix:function(a,c){for(var d=0;d 0) { 43 | property = 'bottom'; 44 | previewer.classList.remove('flipped'); 45 | } 46 | else { 47 | property = 'top'; 48 | previewer.classList.add('flipped'); 49 | } 50 | 51 | style.bottom = style.top = ''; 52 | style[property] = offsets[property] + token.offsetHeight + 'px'; 53 | style.left = offsets.left + Math.min(200, token.offsetWidth/2) + 'px'; 54 | } 55 | } 56 | } 57 | 58 | if(!token || !valid && oldToken) { 59 | previewer.classList.remove('active'); 60 | previewer.style.display = ''; 61 | } 62 | } 63 | }); 64 | } 65 | 66 | _.prototype = { 67 | 68 | }; 69 | 70 | _.s = {}; 71 | 72 | _.hideAll = function(except) { 73 | var all = _.s; 74 | 75 | for(var id in all) { 76 | if(!except || except !== id) { 77 | all[id].token = null; 78 | } 79 | } 80 | }; 81 | 82 | _.get = function(token) { 83 | var type = (token && token.className.match(/^token ([\w-]+)/) || [])[1]; 84 | 85 | return type in _.s? type : null; 86 | } 87 | 88 | _._active = null; 89 | Object.defineProperty(_, 'active', { 90 | get: function() { return this._active; }, 91 | set: function(token) { 92 | var oldToken = this._active; 93 | 94 | this._active = token; 95 | 96 | if(oldToken) { 97 | oldToken.removeAttribute('data-active'); 98 | } 99 | 100 | if(token) { 101 | token.setAttribute('data-active', ''); 102 | } 103 | } 104 | }); 105 | 106 | })(); 107 | 108 | /** 109 | * Define previewers 110 | */ 111 | new Previewer('color', function(code) { 112 | var style = this.style; 113 | 114 | style.backgroundColor = ''; 115 | style.backgroundColor = code; 116 | 117 | return !!style.backgroundColor; 118 | }); 119 | 120 | new Previewer('abslength', function(code) { 121 | var style = this.style, 122 | abs = code.replace(/^-/, ''); 123 | 124 | style.width = ''; 125 | style.width = abs; 126 | 127 | var valid = !!style.width; 128 | 129 | if(valid) { 130 | var num = parseFloat(abs), 131 | unit = (code.match(/[a-z]+$/i) || [])[0]; 132 | 133 | style.marginLeft = -num/2 + unit; 134 | 135 | style.display = 'block'; 136 | 137 | var width = this.offsetWidth; 138 | 139 | if(width > innerWidth) { 140 | valid = false; 141 | } 142 | else { 143 | var size = width < 20? (width < 10? 'tiny' : 'small') : 'normal'; 144 | this.setAttribute('data-size', size); 145 | } 146 | } 147 | 148 | return valid; 149 | }); 150 | 151 | new Previewer('time', function(code) { 152 | if(code === '0s') { 153 | return false; 154 | } 155 | 156 | var num = parseFloat(code), 157 | unit = (code.match(/[a-z]+$/i) || [])[0]; 158 | 159 | $$('animate', this).forEach(function(animation) { 160 | animation.setAttribute('dur', 2*num + unit); 161 | }); 162 | 163 | return true; 164 | }); 165 | 166 | new Previewer('angle', function(code) { 167 | var num = parseFloat(code), 168 | unit = (code.match(/[a-z]+$/i) || [])[0], 169 | max, percentage; 170 | 171 | switch(unit) { 172 | case 'deg': 173 | max = 360; 174 | break; 175 | case 'grad': 176 | max = 400; 177 | break; 178 | case 'rad': 179 | max = 2 * Math.PI; 180 | break; 181 | case 'turn': 182 | max = 1; 183 | } 184 | 185 | percentage = 100 * num/max; 186 | percentage %= 100; 187 | 188 | this[(num < 0? 'set' : 'remove') + 'Attribute']('data-negative', ''); 189 | 190 | $('circle', this).setAttribute('stroke-dasharray', Math.abs(percentage) + ',500') 191 | 192 | return true; 193 | }); 194 | 195 | new Previewer('fontfamily', function(code) { 196 | var style = this.style; 197 | 198 | style.fontFamily = ''; 199 | style.fontFamily = code; 200 | 201 | return !!style.fontFamily; 202 | }); 203 | 204 | new Previewer('gradient', function(code) { 205 | var inner = this.firstChild, 206 | style = inner.style; 207 | 208 | style.cssText = StyleFix.fix('background-image: ' + code); 209 | 210 | return !!style.backgroundImage; 211 | }); 212 | 213 | new Previewer('easing', function(code) { 214 | code = ({ 215 | 'linear': '0,0,1,1', 216 | 'ease': '.25,.1,.25,1', 217 | 'ease-in': '.42,0,1,1', 218 | 'ease-out': '0,0,.58,1', 219 | 'ease-in-out':'.42,0,.58,1' 220 | })[code] || code; 221 | 222 | var p = code.match(/-?\d*\.?\d+/g); 223 | 224 | if(p.length === 4) { 225 | p = p.map(function(p, i) { return (i % 2? 1 - p : p) * 100; }); 226 | 227 | $('path', this).setAttribute('d', 'M0,100 C' + p[0] + ',' + p[1] + ', ' + p[2] + ',' + p[3] + ', 100,0'); 228 | 229 | var lines = $$('line', this); 230 | 231 | lines[0].setAttribute('x2', p[0]); 232 | lines[0].setAttribute('y2', p[1]); 233 | lines[1].setAttribute('x2', p[2]); 234 | lines[1].setAttribute('y2', p[3]); 235 | 236 | return true; 237 | } 238 | 239 | return false; 240 | }); 241 | 242 | new Previewer('url', function(code) { 243 | var href = code.replace(/^url\(('|")?|('|")?\)$/g, ''), 244 | img = $('img',this), 245 | that = this; 246 | 247 | img.src = href; 248 | 249 | img.onload = function() { 250 | this.className = ''; 251 | that.style.marginLeft = '-' + this.offsetWidth/2 + 'px'; 252 | }; 253 | 254 | img.onerror = function() { 255 | this.onload(); 256 | this.className = 'error'; 257 | }; 258 | 259 | return true; 260 | }); 261 | 262 | new Previewer('entity', function(code) { 263 | if(code.charAt(0) === '\\') { 264 | this.textContent = String.fromCharCode(parseInt(code.slice(1), 16)); 265 | } 266 | else { 267 | this.innerHTML = code; 268 | } 269 | 270 | return this.textContent.length === 1; 271 | }); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | dabblet.com 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

dabblet

23 | 24 | Log in 25 | 26 |
27 | 28 | 53 |
54 | 55 |
56 | 57 | 58 | New dabblet 59 | 60 |
61 | 62 | Save 63 | Save as new 64 | Save anonymously 65 | 66 |
67 | 68 | View gist 69 | 70 | 71 |
72 |
73 | 74 |
75 | ? 76 |
77 | 91 |
92 |
93 |
94 | 95 | 96 |
97 |
/**  The first commented line is your dabblet’s title */
 98 | body{
 99 | background: #f06;
100 | background: linear-gradient(45deg, #f06, yellow);
101 | min-height:100px;
102 | }
103 | div{
104 | 	height:150px;
105 | 	width:20px;
106 | 	padding-top:15px;
107 | 	padding-left:25px;
108 | 	background:#f00;
109 | 	border:5px solid #fff;
110 | 	border-radius:15px;
111 | }
112 | 
113 |
114 | 115 |
116 |
<div> </div>
117 | 
118 |
119 | 120 |
121 |
122 |
123 | 124 | 125 | 126 | 127 | 128 | 129 |
130 |
131 | 132 | 133 | 134 |
135 |
(ABCabc123&@%) 136 | (ABCabc123&@%)
137 |
138 |
139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 |
150 |
Loading…
151 |
f
152 | 153 |
Loading…
154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /help/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Help ✿ dabblet.com 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |

dabblet

17 |

help

18 |
19 | 20 |
21 |

dabblet is an interactive playground for quickly testing snippets of CSS and HTML code. 22 | It uses -prefix-free, 23 | so that you won't have to add any prefixes in your CSS code. You can save your work in 24 | Github gists, embed it in other websites and share it 25 | with others.

26 | 27 |

It currently only supports modern versions of Chrome, Safari and Firefox but I'm hoping 28 | to expand browser support soon.

29 | 30 |

It’s handcoded by Lea Verou with care 31 | but some other nice folks helped too. 32 | Special thanks go to Roman Komarov who helped tremendously 33 | with his tips & thorough QA.

34 | 35 | 41 |
42 | 43 | 53 | 54 |
55 |

F.A.Q.

56 | 57 |

Where can I report bugs?

58 |

Please report any bugs and suggest features in dabblet’s bug tracker. 59 | An extensive description and (for bugs) a reduced testcase are appreciated. Thanks for your effort towards making dabblet better!

60 | 61 |

What about JavaScript?

62 |

You may use <script> tags as usual in your HTML markup in order to execute JavaScript code in your dabblet. 63 | However, keep in mind that dabblet is CSS-focused. If your demo needs a lot of JavaScript, you might 64 | find something like jsFiddle 65 | or JSBin more helpful. 66 |

67 | 68 |
69 |

Keyboard shortcuts

70 | 71 |

I’m a heavy keyboard user myself, so lots of effort has been put into making dabblet 72 | a breeze to use with the keyboard:

73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 133 | 134 | 135 |
ActionResult
SSave if not saved, update if saved, fork if not own
1-8Go to first, second, third etc tab, provided they exist.
9Go to the last tab
[Go to previous tab
]Go to next tab
in editing mode
Enters a Tab (\t) character
after selecting text
Indents the selected lines (including partially selected lines)
after selecting text
Removes one indent (Tab or space) from the beginning of each (partially) selected line.
/
in editing mode
Comments the current selection (or line if there is none) or uncomments if within a comment
/ Increment/decrement value by 1
/ Increment/decrement value by 10
/ Increment/decrement value by 0.1
in editing mode
The editing area loses focus, so after pressing you can use 132 | the navigation bar through the keyboard
136 |

Note: Wherever is used, it means either or , in both platforms. 137 | This wasn’t only done for cross-platform compatibility, but also because some browsers don’t allow overriding their default shortcuts.

138 |

In any case, if a keyboard shortcut doesn’t make sense, it’s deferred to the browser (for example 9 when you’re already on the last tab). 139 |

140 | 141 |
142 |

License

143 |

All posted code belongs to the poster and no license is enforced.

144 |

I am not responsible or liable for any loss or damage of any kind during the usage of dabblet or provided code.

145 |

dabblet itself is open source software and is distributed under a 146 | NPOSL-3.0 license. 147 | Fork dabblet on github, we’re waiting for your pull requests!

148 |
149 | 150 |
151 |

Press

152 |

The letters for the dabblet logo and all the icons that dabblet uses can be found in this font. For example usage, refer to the following dabblets: 153 |

157 |
158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /code/utopia.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Utopia: A JavaScript util library that assumes modern standards support and doesn't fix any browser bugs 3 | * @author Lea Verou (http://lea.verou.me) 4 | * @version 0.2 5 | */ 6 | 7 | function $(expr, con) { return (con || document).querySelector(expr); } 8 | function $$(expr, con) { return Array.prototype.slice.call((con || document).querySelectorAll(expr)); } 9 | 10 | // Make each ID a global variable 11 | // Many browsers do this anyway (it’s in the HTML5 spec), so it ensures consistency 12 | $$('[id]').forEach(function(element) { window[element.id] = element; }); 13 | 14 | // Array#splice but for strings 15 | String.prototype.splice = function(i, remove, add) { 16 | remove = +remove || 0; 17 | add = add || ''; 18 | 19 | return this.slice(0,i) + add + this.slice(i + remove); 20 | }; 21 | 22 | (function(){ 23 | 24 | var _ = window.Utopia = { 25 | /** 26 | * Returns the [[Class]] of an object in lowercase (eg. array, date, regexp, string etc) 27 | * Caution: Results for DOM elements and collections aren't reliable. 28 | * @param {Object} obj 29 | * 30 | * @return {String} 31 | */ 32 | type: function(obj) { 33 | if(obj === null) { return 'null'; } 34 | 35 | if(obj === undefined) { return 'undefined'; } 36 | 37 | var ret = Object.prototype.toString.call(obj).match(/^\[object\s+(.*?)\]$/)[1]; 38 | 39 | ret = ret? ret.toLowerCase() : ''; 40 | 41 | if(ret == 'number' && isNaN(obj)) { 42 | return 'NaN'; 43 | } 44 | 45 | return ret; 46 | }, 47 | 48 | /** 49 | * Iterate over the properties of an object. Checks whether the properties actually belong to it. 50 | * Can be stopped if the function explicitly returns a value that isn't null, undefined or NaN. 51 | * 52 | * @param obj {Object} The object to iterate over 53 | * @param func {Function} The function used in the iteration. Can accept 2 parameters: one of the 54 | * value of the object and one for its name. 55 | * @param context {Object} Context for the above function. Default is the object being iterated. 56 | * 57 | * @return {Object} Null or the return value of func, if it broke the loop at some point. 58 | */ 59 | each: function(obj, func, context) { 60 | if(!_.type(func) == 'function') { 61 | throw Error('The second argument in Utopia.each() must be a function'); 62 | }; 63 | 64 | context = context || obj; 65 | 66 | for (var i in obj) { 67 | if(obj.hasOwnProperty && obj.hasOwnProperty(i)) { 68 | var ret = func.call(context, obj[i], i); 69 | 70 | if(!!ret || ret === 0 || ret === '') { 71 | return ret; 72 | } 73 | } 74 | } 75 | 76 | return null; 77 | }, 78 | 79 | /** 80 | * Copies the properties of one object onto another. 81 | * 82 | * @return {Object} destination object 83 | */ 84 | merge: function(objects) { 85 | var ret = {}; 86 | 87 | for(var i=0; i|>)/g, 70 | 'tag': { 71 | 'pattern': /(<|<)\/?[\w\W]+?(>|>)/gi, 72 | 'inside': { 73 | 'attr-value': { 74 | 'pattern': /[\w-]+=(('|").*?(\2)|[^\s>]+(?=>|&|\s))/gi, 75 | 'inside': { 76 | 'attr-name': /^[\w-]+(?==)/gi, 77 | 'punctuation': /=/g 78 | } 79 | }, 80 | 'attr-name': /\s[\w-]+(?=\s)/gi, 81 | 'punctuation': /<\/?|>/g 82 | } 83 | }, 84 | 'entity': /&#?[\da-z]{1,8};/gi 85 | } 86 | }, 87 | 88 | init: function(code, useWorkers, callback) { 89 | if(!code) { 90 | return; 91 | } 92 | 93 | var lang = _.languages[code.getAttribute('lang')]; 94 | 95 | if(!lang) { 96 | return; 97 | } 98 | 99 | var text = code.textContent 100 | .replace(/&/g, '&') 101 | .replace(//g, '>') 103 | .replace(/\u00a0/g, ' '); 104 | 105 | if(useWorkers && self.Worker) { 106 | if(self.worker) { 107 | self.worker.terminate(); 108 | } 109 | 110 | self.worker = new Worker('/code-highlight.js'); 111 | 112 | worker.onmessage = function(evt) { 113 | code.innerHTML = evt.data; 114 | callback && callback(); 115 | }; 116 | 117 | worker.postMessage(code.getAttribute('lang') + '|' + text); 118 | } 119 | else { 120 | code.innerHTML = _.do(text, lang); 121 | callback && callback(); 122 | } 123 | }, 124 | 125 | do: function(text, tokens) { 126 | var strarr = [text]; 127 | 128 | for(var token in tokens) { 129 | var pattern = tokens[token], 130 | inside = pattern.inside; 131 | pattern = pattern.pattern || pattern; 132 | 133 | for(var i=0; i' + content + '' 183 | } 184 | } 185 | 186 | if(!self.document) { 187 | // In worker 188 | self.addEventListener('message', function(evt) { 189 | var message = evt.data, 190 | i = message.indexOf('|'), 191 | lang = message.slice(0,i), 192 | code = message.slice(i+1); 193 | 194 | self.postMessage(_.do(code, _.languages[lang])); 195 | }, false); 196 | } 197 | 198 | // CSS colors 199 | var colors = [ 200 | // 'aliceblue', 201 | // 'antiquewhite', 202 | 'aqua', 203 | // 'aquamarine', 204 | // 'azure', 205 | // 'beige', 206 | // 'bisque', 207 | 'black', 208 | // 'blanchedalmond', 209 | 'blue', 210 | // 'blueviolet', 211 | 'brown', 212 | // 'burlywood', 213 | // 'cadetblue', 214 | // 'chartreuse', 215 | // 'chocolate', 216 | // 'coral', 217 | // 'cornflowerblue', 218 | // 'cornsilk', 219 | // 'crimson', 220 | 'cyan', 221 | // 'darkblue', 222 | // 'darkcyan', 223 | // 'darkgoldenrod', 224 | 'darkgray', 225 | // 'darkgreen', 226 | 'darkgrey', 227 | // 'darkkhaki', 228 | // 'darkmagenta', 229 | // 'darkolivegreen', 230 | // 'darkorange', 231 | // 'darkorchid', 232 | // 'darkred', 233 | // 'darksalmon', 234 | // 'darkseagreen', 235 | // 'darkslateblue', 236 | // 'darkslategray', 237 | // 'darkslategrey', 238 | // 'darkturquoise', 239 | // 'darkviolet', 240 | 'deeppink', 241 | // 'deepskyblue', 242 | 'dimgray', 243 | 'dimgrey', 244 | // 'dodgerblue', 245 | // 'firebrick', 246 | // 'floralwhite', 247 | // 'forestgreen', 248 | 'fuchsia', 249 | // 'gainsboro', 250 | // 'ghostwhite', 251 | 'gold', 252 | // 'goldenrod', 253 | 'gray', 254 | 'green', 255 | // 'greenyellow', 256 | 'grey', 257 | // 'honeydew', 258 | // 'hotpink', 259 | 'indianred', 260 | // 'indigo', 261 | // 'ivory', 262 | // 'khaki', 263 | // 'lavender', 264 | // 'lavenderblush', 265 | // 'lawngreen', 266 | // 'lemonchiffon', 267 | // 'lightblue', 268 | // 'lightcoral', 269 | // 'lightcyan', 270 | // 'lightgoldenrodyellow', 271 | 'lightgray', 272 | // 'lightgreen', 273 | 'lightgrey', 274 | // 'lightpink', 275 | // 'lightsalmon', 276 | // 'lightseagreen', 277 | // 'lightskyblue', 278 | // 'lightslategray', 279 | // 'lightslategrey', 280 | // 'lightsteelblue', 281 | // 'lightyellow', 282 | 'lime', 283 | 'limegreen', 284 | // 'linen', 285 | 'magenta', 286 | 'maroon', 287 | // 'mediumaquamarine', 288 | // 'mediumblue', 289 | // 'mediumorchid', 290 | // 'mediumpurple', 291 | // 'mediumseagreen', 292 | // 'mediumslateblue', 293 | // 'mediumspringgreen', 294 | // 'mediumturquoise', 295 | // 'mediumvioletred', 296 | // 'midnightblue', 297 | // 'mintcream', 298 | // 'mistyrose', 299 | // 'moccasin', 300 | // 'navajowhite', 301 | 'navy', 302 | // 'oldlace', 303 | 'olive', 304 | // 'olivedrab', 305 | 'orange', 306 | 'orangered', 307 | 'orchid', 308 | // 'palegoldenrod', 309 | // 'palegreen', 310 | // 'paleturquoise', 311 | // 'palevioletred', 312 | 'papayawhip', 313 | 'peachpuff', 314 | 'peru', 315 | 'pink', 316 | 'plum', 317 | // 'powderblue', 318 | 'purple', 319 | 'red', 320 | // 'rosybrown', 321 | // 'royalblue', 322 | // 'saddlebrown', 323 | 'salmon', 324 | // 'sandybrown', 325 | // 'seagreen', 326 | // 'seashell', 327 | // 'sienna', 328 | 'silver', 329 | // 'skyblue', 330 | // 'slateblue', 331 | 'slategray', 332 | 'slategrey', 333 | 'snow', 334 | // 'springgreen', 335 | // 'steelblue', 336 | 'tan', 337 | 'teal', 338 | 'thistle', 339 | 'tomato', 340 | 'transparent', 341 | 'turquoise', 342 | 'violet', 343 | 'wheat', 344 | 'white', 345 | 'whitesmoke', 346 | 'yellow', 347 | 'yellowgreen' 348 | ]; 349 | 350 | _.languages.css.color = RegExp.create('\\b{{keyword}}\\b|\\b{{func}}\\B|\\B{{hex}}\\b', { 351 | keyword: RegExp('^' + colors.join('|') + '$'), 352 | func: RegExp.create('^(?:rgb|hsl)a?\\((?:\\s*{{number}}%?\\s*,?\\s*){3,4}\\)$', { 353 | number: number 354 | }), 355 | hex: /^#(?:[0-9a-f]{3}){1,2}$/i 356 | }, 'ig'); 357 | 358 | })(); -------------------------------------------------------------------------------- /code/editor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Code for the code editors 3 | */ 4 | 5 | (function(){ 6 | 7 | var CRLF = crlf = /\r?\n|\r/g; 8 | 9 | var UndoManager = function(editor) { 10 | this.editor = editor; 11 | 12 | this.undoStack = []; 13 | this.redoStack = []; 14 | }; 15 | 16 | UndoManager.prototype = { 17 | action: function(action) { 18 | if(!action || !(action.length || action.action || action.add || action.del)) { 19 | return; 20 | } 21 | 22 | var lastAction = this.undoStack.pop() || null; 23 | 24 | if(lastAction) { 25 | var push = lastAction.action || action.action 26 | || lastAction.length || action.length 27 | || (action.del && lastAction.add) 28 | || (action.add && !lastAction.add) 29 | || (lastAction.start + lastAction.add.length - lastAction.del.length != action.start); 30 | 31 | if(push) { 32 | this.undoStack.push(lastAction); 33 | this.undoStack.push(action); 34 | } 35 | else if(lastAction) { 36 | var combined = this.chain(lastAction, action); 37 | 38 | this.undoStack.push(combined); 39 | } 40 | } 41 | else { 42 | this.undoStack.push(action); 43 | } 44 | 45 | this.redoStack = []; 46 | }, 47 | 48 | undo: function() { 49 | 50 | var action = this.undoStack.pop(); 51 | 52 | if(!action) { 53 | return; 54 | } 55 | 56 | this.redoStack.push(action); 57 | 58 | this.applyInverse(action); 59 | 60 | this.editor.pre.onkeyup(); 61 | }, 62 | 63 | redo: function() { 64 | 65 | var action = this.redoStack.pop(); 66 | 67 | if(!action) { 68 | return; 69 | } 70 | 71 | this.undoStack.push(action); 72 | 73 | this.apply(action); 74 | 75 | this.editor.pre.onkeyup(); 76 | }, 77 | 78 | chain: function(action1, action2) { 79 | return { 80 | add: action1.add + action2.add, 81 | del: action2.del + action1.del, 82 | start: action1.start 83 | } 84 | }, 85 | 86 | apply: function(action) { 87 | if(action.length) { 88 | for(var i=0; i=0; i--) { 117 | this.applyInverse(action[i]); 118 | } 119 | return; 120 | } 121 | 122 | var start = action.start; 123 | 124 | if(action.action) { 125 | this.editor.action(action.action, { 126 | inverse: !action.inverse, 127 | start: start, 128 | end: action.end, 129 | noHistory: true 130 | }); 131 | } 132 | else { 133 | var element = this.editor.pre; 134 | 135 | // remove added chars & add deleted chars 136 | element.textContent = element.textContent.splice(start, action.add.length, action.del); 137 | 138 | element.setSelectionRange(start, start + action.del.length); 139 | } 140 | } 141 | }; 142 | 143 | var _ = window.Editor = function(pre) { 144 | var that = this; 145 | 146 | this.pre = pre; 147 | this.parent = pre.parentNode; 148 | this.lang = pre.getAttribute('lang'); 149 | 150 | this.lineHighlight = document.createElement('div'); 151 | this.lineHighlight.className = 'line-highlight'; 152 | 153 | this.parent.insertBefore(this.lineHighlight, this.pre); 154 | 155 | this.undoManager = new UndoManager(this); 156 | 157 | $u.event.bind(pre, { 158 | keydown: function(evt) { 159 | var cmdOrCtrl = evt.metaKey || evt.ctrlKey; 160 | 161 | switch(evt.keyCode) { 162 | case 8: // Backspace 163 | var ss = this.selectionStart, 164 | se = this.selectionEnd, 165 | length = ss === se? 1 : Math.abs(se - ss), 166 | start = se - length; 167 | 168 | that.undoManager.action({ 169 | add: '', 170 | del: this.textContent.slice(start, se), 171 | start: start 172 | }); 173 | 174 | break; 175 | case 9: // Tab 176 | if(!cmdOrCtrl) { 177 | that.action('indent', { 178 | inverse: evt.shiftKey 179 | }); 180 | return false; 181 | } 182 | case 13: 183 | that.action('newline'); 184 | return false; 185 | case 90: 186 | if(cmdOrCtrl) { 187 | that.undoManager[evt.shiftKey? 'redo' : 'undo'](); 188 | return false; 189 | } 190 | 191 | break; 192 | case 191: 193 | if(cmdOrCtrl) { 194 | that.action('comment', { lang: this.id }); 195 | return false; 196 | } 197 | 198 | break; 199 | } 200 | }, 201 | 202 | keypress: function(evt) { 203 | var cmdOrCtrl = evt.metaKey || evt.ctrlKey, 204 | code = evt.charCode, 205 | ss = this.selectionStart, 206 | se = this.selectionEnd; 207 | 208 | if(code && !cmdOrCtrl) { 209 | var character = String.fromCharCode(code); 210 | 211 | that.undoManager.action({ 212 | add: character, 213 | del: ss === se? '' : this.textContent.slice(ss, se), 214 | start: ss 215 | }); 216 | } 217 | }, 218 | 219 | cut: function() { 220 | ss = this.selectionStart, 221 | se = this.selectionEnd, 222 | selection = ss === se? '': this.textContent.slice(ss, se); 223 | 224 | if(selection) { 225 | that.undoManager.action({ 226 | add: '', 227 | del: selection, 228 | start: ss 229 | }); 230 | 231 | gist.saved = false; 232 | } 233 | }, 234 | 235 | paste: function() { 236 | var pre = this, 237 | ss = pre.selectionStart, 238 | se = pre.selectionEnd, 239 | selection = ss === se? '': pre.textContent.slice(ss, se); 240 | 241 | gist.saved = false; 242 | 243 | setTimeout(function(){ 244 | var newse = pre.selectionEnd, 245 | innerHTML = pre.innerHTML; 246 | 247 | innerHTML = pre.innerHTML 248 | .replace(/(<\w+)(\s.+?>)/g, '$1>') 249 | .replace(/<\/?pre>/g, '') 250 | .replace(/(
)?
|(
)+/gi, '\n') 251 | .replace(/<\/div>/gi, '') 252 | .replace(/ /gi, ' '); 253 | 254 | pre.innerHTML = innerHTML; 255 | 256 | var pasted = pre.textContent.slice(ss, newse); 257 | 258 | that.undoManager.action({ 259 | add: pasted, 260 | del: selection, 261 | start: ss 262 | }); 263 | 264 | ss += pasted.length; 265 | 266 | pre.setSelectionRange(ss, ss); 267 | 268 | pre.onkeyup(); 269 | }, 10); 270 | }, 271 | 272 | keyup: function(evt) { 273 | var keyCode = evt && evt.keyCode || 0, 274 | code = this.textContent, 275 | id = this.id; 276 | 277 | if(keyCode <= 8 || keyCode == 13 || keyCode > 32 && keyCode < 41) { 278 | $u.event.fire(this, 'caretmove'); 279 | } 280 | 281 | if([ 282 | 9, 91, 93, 16, 17, 18, // modifiers 283 | 20, // caps lock 284 | 13, // Enter (handled by keydown) 285 | 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, // F[0-12] 286 | 27 // Esc 287 | ].indexOf(keyCode) > -1) { 288 | return; 289 | } 290 | 291 | if(keyCode !== 37 && keyCode !== 39) { 292 | var ss = this.selectionStart, 293 | se = this.selectionEnd; 294 | 295 | Highlight.init(this); 296 | 297 | // Dirty fix to #2 298 | if(!/\n$/.test(code)) { 299 | this.innerHTML = this.innerHTML + '\n'; 300 | } 301 | 302 | if(ss !== null || se !== null) { 303 | this.setSelectionRange(ss, se); 304 | } 305 | 306 | if(id === 'css') { 307 | document.title = Dabblet.title(code) + ' ✿ dabblet.com'; 308 | 309 | Dabblet.update.CSS(code); 310 | } 311 | else { 312 | Dabblet.update.HTML(code); 313 | } 314 | 315 | if(keyCode) { 316 | gist.saved = false; 317 | } 318 | } 319 | }, 320 | 321 | click: function(evt) { 322 | $u.event.fire(this, 'caretmove'); 323 | }, 324 | 325 | focus: function() { 326 | var ss = this.getAttribute('data-ss'), 327 | se = this.getAttribute('data-se'); 328 | 329 | if(ss || se) { 330 | var pre = this; 331 | setTimeout(function(){ 332 | pre.setSelectionRange(ss, se); 333 | }, 2); 334 | } 335 | 336 | if(this.id == 'css' && !window.Incrementable) { 337 | $u.script('code/incrementable.js', function() { 338 | new Incrementable(css, function(evt) { 339 | if(evt.altKey) { 340 | if(evt.shiftKey) { return 10; } 341 | 342 | if(evt.ctrlKey) { return .1; } 343 | 344 | return 1; 345 | } 346 | return 0; 347 | }); 348 | }); 349 | } 350 | }, 351 | 352 | blur: function() { 353 | if(!gist.saved) { 354 | // Save draft 355 | localStorage['dabblet.css'] = css.textContent; 356 | localStorage['dabblet.html'] = html.textContent; 357 | } 358 | 359 | self.Previewer && Previewer.hideAll(); 360 | }, 361 | 362 | mouseover: function(evt) { 363 | if(!self.Previewer) { 364 | return; 365 | } 366 | /* prevent hovering other elements while resizing current one - alex */ 367 | if(scrubbingValues.ctrlMode){ 368 | return; 369 | } 370 | 371 | var target = evt.target, 372 | type = Previewer.get(target); 373 | 374 | if (type) { 375 | 376 | var previewer = Previewer.s[type]; 377 | 378 | if (previewer.token != target) { 379 | previewer.token = target; 380 | 381 | target.onmouseout = function() { 382 | previewer.token = this.onmouseout = null; 383 | 384 | // Show the previewer again on the active token 385 | var active = Previewer.active; 386 | 387 | if (active) { 388 | var type = Previewer.get(active); 389 | Previewer.s[type].token = active; 390 | } 391 | } 392 | } 393 | } 394 | } 395 | }, true); 396 | 397 | $u.event.bind(pre, 'caretmove', function() { 398 | var content = this.textContent, 399 | ss = this.selectionStart, 400 | se = this.selectionEnd; 401 | 402 | ss !== null && this.setAttribute('data-ss', ss); 403 | se !== null && this.setAttribute('data-se', se); 404 | 405 | // Update current line highlight 406 | var highlighter = that.lineHighlight, 407 | lines = (content.match(CRLF) || []).length, 408 | line = (content.slice(0, ss).match(CRLF) || []).length, 409 | lineHeight = parseFloat(getComputedStyle(this).height)/lines; 410 | 411 | highlighter.setAttribute('data-line', line + 1); 412 | highlighter.style.height = lineHeight + 'px'; 413 | highlighter.style.top = line * lineHeight + 'px'; 414 | 415 | // Show a previewer, if needed 416 | if(self.Previewer) { 417 | var selection = getSelection(); 418 | 419 | if(selection.rangeCount) { 420 | var range = selection.getRangeAt(0), 421 | element = range.startContainer; 422 | 423 | if(element.nodeType == 3) { 424 | element = element.parentNode; 425 | } 426 | 427 | var type = Previewer.get(element); 428 | 429 | if(type) { 430 | Previewer.active = element; 431 | Previewer.s[type].token = element; 432 | } 433 | else { 434 | Previewer.hideAll(); 435 | Previewer.active = null; 436 | } 437 | } 438 | } 439 | }); 440 | 441 | $u.event.fire(this.pre, 'caretmove'); 442 | }; 443 | 444 | _.prototype = { 445 | action: function(action, options) { 446 | options = options || {}; 447 | 448 | var pre = this.pre, 449 | text = pre.textContent, 450 | ss = options.start || pre.selectionStart, 451 | se = options.end || pre.selectionEnd, 452 | state = { 453 | ss: ss, 454 | se: se, 455 | before: text.slice(0, ss), 456 | after: text.slice(se), 457 | selection: text.slice(ss,se) 458 | }; 459 | 460 | var textAction = _.actions[action](state, options); 461 | 462 | pre.textContent = state.before + state.selection + state.after; 463 | 464 | if(textAction && !options.noHistory) { 465 | this.undoManager.action(textAction); 466 | } 467 | 468 | pre.setSelectionRange(state.ss, state.se); 469 | 470 | pre.onkeyup(); 471 | } 472 | }; 473 | 474 | _.actions = { 475 | indent: function(state, options) { 476 | var lf = state.before.lastIndexOf('\n') + 1; 477 | 478 | if (options.inverse) { 479 | if(/\s/.test(state.before.charAt(lf))) { 480 | state.before = state.before.splice(lf, 1); 481 | 482 | state.ss--; 483 | state.se--; 484 | } 485 | 486 | state.selection = state.selection.replace(/\r?\n\s/g, '\n'); 487 | } 488 | else if (state.selection) { 489 | state.before = state.before.splice(lf, 0, '\t'); 490 | state.selection = state.selection.replace(/\r?\n/g, '\n\t'); 491 | 492 | state.ss++; 493 | state.se++; 494 | } 495 | else { 496 | state.before += '\t'; 497 | 498 | state.ss++; 499 | state.se++; 500 | 501 | return { 502 | add: '\t', 503 | del: '', 504 | start: state.ss - 1 505 | }; 506 | } 507 | 508 | state.se = state.ss + state.selection.length; 509 | 510 | return { 511 | action: 'indent', 512 | start: state.ss, 513 | end: state.se, 514 | inverse: options.inverse 515 | }; 516 | }, 517 | 518 | newline: function(state) { 519 | var ss = state.ss, 520 | lf = state.before.lastIndexOf('\n') + 1, 521 | indent = (state.before.slice(lf).match(/^\s+/) || [''])[0]; 522 | 523 | state.before += '\n' + indent; 524 | state.selection = ''; 525 | 526 | state.ss += indent.length + 1; 527 | state.se = state.ss; 528 | 529 | return { 530 | add: '\n' + indent, 531 | del: state.selection, 532 | start: ss 533 | }; 534 | }, 535 | 536 | comment: function(state, options) { 537 | var open = options.lang === 'css'? '/*' : ''; 539 | 540 | var start = state.before.lastIndexOf(open), 541 | end = state.after.indexOf(close), 542 | closeBefore = state.before.lastIndexOf(close), 543 | openAfter = state.after.indexOf(start); 544 | 545 | if(start > -1 && end > -1 546 | && (start > closeBefore || closeBefore === -1) 547 | && (end < openAfter || openAfter === -1) 548 | ) { 549 | // Uncomment 550 | state.before = state.before.splice(start, open.length); 551 | state.after = state.after.splice(end, close.length); 552 | 553 | var textAction = [{ 554 | add: '', 555 | del: open, 556 | start: start 557 | }, { 558 | add: '', 559 | del: close, 560 | start: state.before.length + state.selection.length + end 561 | }]; 562 | 563 | state.ss -= open.length; 564 | state.se -= open.length; 565 | 566 | return textAction; 567 | } 568 | else { 569 | // Comment 570 | if(state.selection) { 571 | // Comment selection 572 | state.selection = open + state.selection + close; 573 | 574 | textAction = [{ 575 | add: open, 576 | del: '', 577 | start: state.ss 578 | }, { 579 | add: close, 580 | del: '', 581 | start: open.length + state.se 582 | }]; 583 | } 584 | else { 585 | // Comment whole line 586 | var start = state.before.lastIndexOf('\n') + 1, 587 | end = state.after.indexOf('\n'); 588 | 589 | if(end === -1) { 590 | end = after.length; 591 | } 592 | 593 | while(/\s/.test(state.before.charAt(start))) { 594 | start++; 595 | } 596 | 597 | state.before = state.before.splice(start, 0, open); 598 | 599 | state.after = state.after.splice(end, 0, close); 600 | 601 | var textAction = [{ 602 | add: open, 603 | del: '', 604 | start: start 605 | }, { 606 | add: close, 607 | del: '', 608 | start: state.before.length + end 609 | }]; 610 | } 611 | 612 | state.ss += open.length; 613 | state.se += open.length; 614 | 615 | return textAction; 616 | } 617 | } 618 | } 619 | 620 | })(); -------------------------------------------------------------------------------- /code/dabblet.js: -------------------------------------------------------------------------------- 1 | var gist = { 2 | clientId: 'da931d37076424f332ef', 3 | 4 | oauth: [ 5 | // Step 1: Ask permission 6 | function(callback){ 7 | gist.oauth.callback = callback; 8 | 9 | var popup = open('https://github.com/login/oauth/authorize' + 10 | '?client_id=' + gist.clientId + 11 | '&scope=gist', 'popup', 'width=1015,height=500'); 12 | }, 13 | // Step 2: Get access token and store it 14 | function(token){ 15 | if(token) { 16 | window.ACCESS_TOKEN = localStorage['access_token'] = token; 17 | 18 | gist.getUser(gist.oauth.callback); 19 | 20 | } 21 | else { 22 | alert('Authentication error'); 23 | } 24 | 25 | gist.oauth.callback = null; 26 | } 27 | ], 28 | 29 | request: function(o) { 30 | o.method = o.method || 'GET'; 31 | o.id = o.id || ''; 32 | o.rev = o.rev || ''; 33 | 34 | var anon = o.anon || o.method === 'GET'; 35 | 36 | if(!anon && !ACCESS_TOKEN) { 37 | gist.oauth[0](function(){ 38 | gist.request(o); 39 | }); 40 | return; 41 | } 42 | 43 | var path = o.path || 'gists' + 44 | (o.id? '/' + o.id : '') + 45 | (o.rev? '/' + o.rev : '') + 46 | (o.gpath || ''); 47 | 48 | $u.xhr({ 49 | method: o.method, 50 | url: 'https://api.github.com/' + path + (!o.anon && ACCESS_TOKEN? '?access_token=' + ACCESS_TOKEN : ''), 51 | headers: o.headers, 52 | callback: function(xhr) { 53 | var data = JSON.parse(xhr.responseText); 54 | 55 | if(data.message) { 56 | alert('Sorry, I got a ' + xhr.status + ' (' + data.message + ')'); 57 | } 58 | else { 59 | o.callback && o.callback(data, xhr); 60 | } 61 | }, 62 | data: o.data? JSON.stringify(o.data) : null 63 | }); 64 | }, 65 | 66 | getUser: function(callback) { 67 | gist.request({ 68 | path: 'user', 69 | callback: function(data) { 70 | window.user = data; 71 | 72 | var login = user.login; 73 | 74 | currentuser.innerHTML = gist.getUserHTML(user); 75 | currentuser.href = gist.getUserURL(user); 76 | 77 | window['save-button'].onclick = window['save-cmd'].onclick = gist.save; 78 | window['save-cmd'].removeAttribute('data-disabled'); 79 | window['save-new-cmd'].removeAttribute('data-disabled'); 80 | 81 | callback && callback(); 82 | } 83 | }); 84 | }, 85 | 86 | getUserHTML: function(user) { 87 | return '' + (user.name || user.login); 88 | }, 89 | 90 | getUserURL: function(user) { 91 | return 'https://gist.github.com/' + user.login; 92 | }, 93 | 94 | save: function(options){ 95 | options = options || {}; 96 | 97 | var anonymous = options.anon || !window.user; 98 | 99 | if(gist.id 100 | && (!gist.user || !window.user || gist.user.id != user.id) 101 | && !anonymous 102 | ) { 103 | // If it doesn't belong to current user, fork first 104 | gist.fork(gist.id, gist.save, options.anon); 105 | return; 106 | } 107 | 108 | var id = gist.id || '', 109 | cssCode = css.textContent, 110 | htmlMarkup = html.textContent, 111 | title = Dabblet.title(cssCode); 112 | 113 | gist.request({ 114 | anon: options.anon, 115 | id: anonymous || options.forceNew? null : id, 116 | method: 'POST', 117 | headers: { 118 | 'Content-Type': 'text/plain; charset=UTF-8' 119 | }, 120 | callback: function(data, xhr) { 121 | if(data.id) { 122 | gist.update(data); 123 | } 124 | }, 125 | data: { 126 | "description": title, 127 | "public": true, 128 | "files": { 129 | "dabblet.css": { 130 | "content": cssCode 131 | }, 132 | "dabblet.html": htmlMarkup? { 133 | "content": htmlMarkup 134 | } : null, 135 | "settings.json": { 136 | "content": JSON.stringify(Dabblet.settings.current(null, 'file')) 137 | } 138 | } 139 | } 140 | }); 141 | }, 142 | 143 | fork: function(id, callback, anon) { 144 | gist.request({ 145 | method: 'POST', 146 | gpath: '/fork', 147 | id: id || gist.id || null, 148 | headers: { 149 | 'Content-Type': 'text/plain; charset=UTF-8' 150 | }, 151 | callback: function(data, xhr) { 152 | if(data.id) { 153 | gist.update(data); 154 | 155 | callback && callback(); 156 | } 157 | }, 158 | data: {} 159 | }); 160 | }, 161 | 162 | load: function(id, rev){ 163 | gist.request({ 164 | id: id || gist.id, 165 | rev: rev || gist.rev, 166 | callback: function(data){ 167 | gist.update(data); 168 | 169 | var files = data.files; 170 | 171 | var cssFile = files['dabblet.css'], 172 | htmlFile = files['dabblet.html'], 173 | settings = files['settings.json']; 174 | 175 | if(!cssFile || !htmlFile) { 176 | for(var filename in files) { 177 | var ext = filename.slice(filename.lastIndexOf('.')); 178 | 179 | if(!cssFile && ext == '.css') { 180 | cssFile = files[filename]; 181 | } 182 | 183 | if(!htmlFile && ext == '.html') { 184 | htmlFile = files[filename]; 185 | } 186 | 187 | if(cssFile && htmlFile) { 188 | break; 189 | } 190 | } 191 | } 192 | 193 | if(htmlFile) { 194 | html.textContent = htmlFile.content; 195 | html.onkeyup(); 196 | } 197 | 198 | if(cssFile) { 199 | css.textContent = cssFile.content; 200 | css.onkeyup(); 201 | } 202 | 203 | var defaultSettings = Dabblet.settings.current(); 204 | 205 | if(typeof localStorage.settings === 'string') { 206 | defaultSettings = $u.merge(defaultSettings, JSON.parse(localStorage.settings)); 207 | } 208 | 209 | if(settings) { 210 | try { settings = JSON.parse(settings.content); } 211 | catch(e) { settings = {}; } 212 | } 213 | else { 214 | settings = {}; 215 | } 216 | 217 | Dabblet.settings.apply($u.merge(defaultSettings, settings)); 218 | } 219 | }); 220 | }, 221 | 222 | update: function(data) { 223 | var id = data.id, 224 | rev = data.history[0].version || ''; 225 | 226 | if(gist.id != id) { 227 | gist.id = id; 228 | gist.rev = undefined; 229 | 230 | history.pushState(null, '', '/gist/' + id + location.search + location.hash); 231 | } 232 | else if(gist.rev && gist.rev !== rev) { 233 | gist.rev = rev; 234 | 235 | history.pushState(null, '', '/gist/' + id + '/' + rev + location.search + location.hash); 236 | } 237 | 238 | if(data.user) { 239 | gist.user = data.user; 240 | } 241 | 242 | var gistUser = window['gist-user']; 243 | if(gist.user && gist.user != window.user) { 244 | gistUser.innerHTML = gist.getUserHTML(gist.user); 245 | gistUser.href = gist.getUserURL(gist.user); 246 | gistUser.removeAttribute('aria-hidden'); 247 | } 248 | else { 249 | gistUser.setAttribute('aria-hidden', 'true'); 250 | } 251 | 252 | $$('a[data-href*="{gist-id}"]').forEach(function(a) { 253 | a.href = a.getAttribute('data-href') 254 | .replace(/\{gist-id\}/gi, id) 255 | .replace(/\{gist-rev\}/gi, rev); 256 | a.removeAttribute('data-disabled'); 257 | a.removeAttribute('aria-hidden'); 258 | }); 259 | 260 | gist.saved = true; 261 | }, 262 | 263 | _saved: true 264 | }; 265 | 266 | Object.defineProperty(gist, 'saved', { 267 | get: function() { 268 | return this._saved; 269 | }, 270 | 271 | set: function(saved) { 272 | saved = !!saved; 273 | 274 | if(saved === this._saved) { 275 | return; 276 | } 277 | 278 | this._saved = saved; 279 | 280 | if(saved) { 281 | document.body.removeAttribute('data-unsaved'); 282 | window['save-cmd'].setAttribute('data-disabled', ''); 283 | } 284 | else { 285 | document.body.setAttribute('data-unsaved', ''); 286 | 287 | if(window.user) { 288 | window['save-cmd'].removeAttribute('data-disabled'); 289 | } 290 | } 291 | } 292 | }); 293 | 294 | var Dabblet = { 295 | version: '1.0.6', 296 | 297 | pages: { 298 | css: window['css-page'], 299 | html: window['html-page'], 300 | result: result 301 | }, 302 | 303 | title: function(code) { 304 | return (code && code.match(/^\/\*[\s\*\r\n]+(.+?)($|\*\/)/m) || [,'Untitled'])[1]; 305 | }, 306 | 307 | wipe: function() { 308 | var question = 'Are you sure? You will lose ' + 309 | (gist.saved? '' : 'unsaved changes and ') + 310 | 'your local draft.'; 311 | 312 | if(confirm(question)) { 313 | localStorage.removeItem('dabblet.css'); 314 | localStorage.removeItem('dabblet.html'); 315 | window.onbeforeunload = null; 316 | return true; 317 | } 318 | 319 | return false; 320 | }, 321 | 322 | get popup() { 323 | return popup.src; 324 | }, 325 | 326 | set popup(url) { 327 | if(url) { 328 | popup.src = url; 329 | popup.parentNode.style.display = 'block'; 330 | } 331 | else { 332 | popup.src = ''; 333 | popup.parentNode.style.display = ''; 334 | } 335 | }, 336 | 337 | update: { 338 | CSS: function(code) { 339 | if(!result.contentWindow.style) { 340 | return; 341 | } 342 | 343 | var style = result.contentWindow.style; 344 | 345 | if(style) { 346 | code = code || css.textContent; 347 | 348 | var title = Dabblet.title(code), 349 | raw = code.indexOf('{') > -1; 350 | 351 | result.contentWindow.document.title = title + ' ✿ Dabblet result'; 352 | 353 | if(!raw) { 354 | code = 'html{' + code + '}'; 355 | } 356 | 357 | var prefixfree = !!Dabblet.settings.cached.prefixfree; 358 | 359 | style.textContent = prefixfree? StyleFix.fix(code, raw) : code; 360 | } 361 | }, 362 | 363 | HTML: function(code) { 364 | if(result.contentDocument.body) { 365 | result.contentDocument.body.innerHTML = code; 366 | } 367 | } 368 | }, 369 | 370 | settings: { 371 | cached: {}, 372 | 373 | handlers: { 374 | page: function(page) { 375 | var currentid = document.body.getAttribute('data-page'), 376 | current = window[currentid], 377 | input = window['page-' + page], 378 | pre = window[page]; 379 | 380 | if(current == pre) { 381 | return; 382 | } 383 | 384 | if(current) { 385 | var ss = current.selectionStart, 386 | se = current.selectionEnd; 387 | 388 | ss && current.setAttribute('data-ss', ss); 389 | se && current.setAttribute('data-se', se); 390 | } 391 | 392 | if(input.value != page || input.checked === false) { 393 | input.click(); 394 | } 395 | 396 | document.body.setAttribute('data-page', page); 397 | 398 | self.Previewer && Previewer.hideAll(); 399 | 400 | pre.focus && pre.focus(); 401 | 402 | var ss = pre.getAttribute('data-ss'), 403 | se = pre.getAttribute('data-se'); 404 | 405 | if((ss || se) && pre.setSelectionRange) { 406 | setTimeout(function(){ 407 | pre.setSelectionRange(ss, se); 408 | }, 2); 409 | } 410 | }, 411 | 412 | prefixfree: function(enabled) { 413 | Dabblet.settings.cached.prefixfree = enabled; 414 | 415 | if(result.contentWindow.style) { 416 | Dabblet.update.CSS(); 417 | } 418 | } 419 | }, 420 | 421 | current: function(name, scope) { 422 | var settings = {}; 423 | 424 | var selector = 'input[data-scope' + 425 | (scope? '="' + scope + '"' : '') + ']' + 426 | (name? '[name="' + name + '"]' : ''); 427 | 428 | $$(selector).forEach(function(input){ 429 | if(!(input.name in settings)) { 430 | // Assign default value 431 | if('checked' in input) { 432 | settings[input.name] = input.hasAttribute('checked')? input.value : ''; 433 | } 434 | } 435 | 436 | if(!('checked' in input) || input.checked) { 437 | settings[input.name] = input.value; 438 | } 439 | else if(input.type === 'checkbox') { 440 | settings[input.name] = ''; 441 | } 442 | }); 443 | 444 | return name? settings[name] : settings; 445 | }, 446 | 447 | apply: function() { 448 | var settings; 449 | 450 | if(arguments.length === 0) { 451 | settings = this.current(); 452 | } 453 | else if(arguments.length === 1) { 454 | settings = arguments[0]; 455 | } 456 | else { 457 | settings = {}; 458 | settings[arguments[0]] = arguments[1]; 459 | } 460 | 461 | for(var name in settings) { 462 | this.applyOne(name, settings[name]); 463 | } 464 | 465 | // Set body classes for each setting 466 | $$('input[data-scope]').forEach(function(input){ 467 | var name = input.name; 468 | 469 | (input.onclick = function(evt){ 470 | switch(this.type) { 471 | case 'radio': 472 | if(this.checked) { 473 | Dabblet.settings.applyOne(name, this.value); 474 | } 475 | return; 476 | case 'checkbox': 477 | Dabblet.settings.applyOne(name, this.checked? this.value : ''); 478 | return; 479 | default: 480 | Dabblet.settings.applyOne(name, this.value); 481 | return; 482 | } 483 | }).call(input); 484 | }); 485 | 486 | // Update cached settings 487 | this.cached = this.current(); 488 | }, 489 | 490 | applyOne: function(name, value) { 491 | var current = this.current(name), 492 | controls = document.getElementsByName(name); 493 | 494 | for(var i=0; i a'); 585 | 586 | if(parent !== window) { 587 | document.body.setAttribute('data-embedded', '') 588 | 589 | a.href = ''; 590 | a.target = '_blank'; 591 | a.title = 'Go to full page dabblet'; 592 | } 593 | else { 594 | a.onclick = Dabblet.wipe; 595 | a.title = 'New dabblet'; 596 | } 597 | 598 | var path = location.pathname.slice(1); 599 | 600 | if(path) { 601 | // Viewing a gist? 602 | if(gist.id = (path.match(/\bgist\/([\da-f]+)/i) || [])[1]) { 603 | gist.rev = (path.match(/\bgist\/[\da-f]+\/([\da-f]+)/i) || [])[1]; 604 | css.textContent = html.textContent = ''; 605 | gist.load(); 606 | } 607 | } 608 | 609 | if(!gist.id) { 610 | if(typeof localStorage['dabblet.css'] === 'string') { 611 | css.textContent = localStorage['dabblet.css']; 612 | } 613 | 614 | if(typeof localStorage['dabblet.html'] === 'string') { 615 | html.textContent = localStorage['dabblet.html']; 616 | } 617 | 618 | if(typeof localStorage.settings === 'string') { 619 | Dabblet.settings.apply(JSON.parse(localStorage.settings)); 620 | } 621 | else { 622 | Dabblet.settings.apply(); 623 | } 624 | } 625 | }); 626 | 627 | $$('.editor.page > pre').forEach(function(pre){ 628 | new Editor(pre); 629 | }); 630 | 631 | // Note: Has to be keydown to be able to cancel the event 632 | document.onkeydown = function(evt) { 633 | var code = evt.keyCode, 634 | character = String.fromCharCode(code), 635 | cmdOrCtrl = evt.metaKey || evt.ctrlKey; 636 | 637 | if(cmdOrCtrl && !evt.altKey) { 638 | switch(character) { 639 | case 'S': 640 | gist.save(); 641 | return false; 642 | case 'N': 643 | if(Dabblet.wipe()) { 644 | location.pathname = '/'; 645 | } 646 | return false; 647 | case '1': 648 | var page = 'css'; 649 | break; 650 | case '2': 651 | var page = 'html'; 652 | break; 653 | case '3': 654 | var page = 'result'; 655 | break; 656 | } 657 | 658 | var currentPage = Dabblet.settings.current('page'); 659 | 660 | if(evt.shiftKey) { 661 | if(code === 219) { 662 | // Go to previous tab 663 | var page = ({ 664 | 'html': 'css', 665 | 'result': 'html' 666 | })[currentPage]; 667 | } 668 | else if (character === ']' || code === 221) { 669 | // Go to next tab 670 | var page = ({ 671 | 'css': 'html', 672 | 'html': 'result' 673 | })[currentPage]; 674 | } 675 | } 676 | 677 | if(page) { 678 | if(currentPage !== page) { 679 | Dabblet.settings.apply('page', page); 680 | 681 | evt.stopPropagation(); 682 | return false; 683 | } 684 | } 685 | } 686 | 687 | if(code == 27) { // Esc 688 | var active = document.activeElement; 689 | 690 | if (active && active != document.body && active.blur) { 691 | active.blur(); 692 | return false; 693 | } 694 | else if (location.hash) { 695 | location.hash = ''; 696 | } 697 | } 698 | 699 | if(code == 112) { // F1 700 | location.hash = '#help'; 701 | return false; 702 | } 703 | }; 704 | 705 | // If only :focus and :checked bubbled... 706 | (function() { 707 | function ancestorClass(action, className, element) { 708 | var ancestor = element; 709 | 710 | do { 711 | ancestor = ancestor.parentNode; 712 | ancestor.classList[action](className) 713 | } while(ancestor && ancestor != document.body); 714 | } 715 | 716 | $u.event.bind('header a, header input, header button, header [tabindex="0"], pre', { 717 | focus: function(){ 718 | ancestorClass('add', 'focus', this); 719 | }, 720 | 721 | blur: function() { 722 | ancestorClass('remove', 'focus', this); 723 | } 724 | }); 725 | })(); -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Dabblet'; 3 | src: url(img/dabblet.ttf); 4 | } 5 | 6 | @keyframes bugfix { from { padding: 0; } to { padding: 0; } } 7 | 8 | /** 9 | * Variables 10 | */ 11 | header, 12 | .popup, 13 | .dropdown { 14 | background: hsla(200, 10%, 20%, .9) url(img/noise.png); 15 | color: white; 16 | text-shadow: 0 -1px 1px black, 0 -1px 2px black; 17 | } 18 | 19 | .popup, 20 | .dropdown { 21 | border: 1px solid rgba(0,0,0,.4); 22 | border-radius: .5em; 23 | box-shadow: 0 1px rgba(255,255,255,.3) inset, 24 | .2em .2em .6em black; 25 | } 26 | 27 | .popup > .content { 28 | background: hsl(24, 20%, 95%) url(img/noise.png); 29 | color: black; 30 | text-shadow: 0 1px white; 31 | box-shadow: 0 .1em .5em rgba(0,0,0,.8) inset; 32 | } 33 | 34 | .popup > .close, 35 | menu > .command:not([data-disabled]) { 36 | transition: .5s box-shadow; 37 | } 38 | 39 | .popup > .close:hover, 40 | menu > .command:not([data-disabled]):hover { 41 | background: rgba(0,0,0,.3); 42 | box-shadow: .05em .05em .25em black inset; 43 | } 44 | 45 | .popup > .close:active, 46 | menu > .command:not([data-disabled]):active { 47 | background: rgba(0,0,0,.4); 48 | box-shadow: .15em .15em .15em black inset; 49 | } 50 | 51 | #easing > svg, 52 | #fontfamily, 53 | #entity { 54 | background: url(img/noise.png), linear-gradient(hsla(200, 10%, 20%, .8), hsl(200, 10%, 20%)); 55 | } 56 | 57 | /** 58 | * Styles 59 | */ 60 | 61 | * { 62 | margin: 0; 63 | padding: 0; 64 | } 65 | 66 | html { 67 | height: 100%; 68 | font: 100%/1.5 "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif; 69 | } 70 | 71 | body { 72 | position: relative; 73 | height: 100%; 74 | border-top: 10px solid transparent; 75 | box-sizing: border-box; 76 | } 77 | 78 | a { 79 | color: inherit; 80 | text-decoration: none; 81 | } 82 | 83 | fieldset { 84 | padding: .5em; 85 | border: 0; 86 | margin-top: .5em; 87 | } 88 | 89 | [aria-hidden="true"] { 90 | display: none !important; 91 | } 92 | 93 | input:focus + label[for], 94 | label.focus { 95 | outline: auto; 96 | outline: 2px auto -webkit-focus-ring-color; 97 | } 98 | 99 | button, .button { 100 | padding: .45em .6em; 101 | border: 1px solid rgba(0,0,0,.4); 102 | border-radius: .3em; 103 | white-space: nowrap; 104 | text-decoration: none; 105 | background-image: url(img/noise.png); 106 | background-color: inherit; 107 | color: inherit; 108 | font: inherit; 109 | box-shadow: rgba(255, 255, 255, .4) 0 1px 0 inset, 110 | rgba(255, 255, 255, .3) 0 25px 30px -12px inset, 111 | rgba(0, 0, 0, .6) 0 1px 2px; 112 | text-shadow: 0 -1px 1px black, 0 -1px 2px black; 113 | cursor: pointer; 114 | } 115 | 116 | button.danger, 117 | .button.danger { 118 | background-color: rgba(196, 0, 0, .8); 119 | text-shadow: 0 -1px 1px rgba(0,0,0,.5), 0 -1px 2px rgba(0,0,0,.5); 120 | } 121 | 122 | button.danger:hover, 123 | .button.danger:hover { 124 | background-color: red; 125 | } 126 | 127 | input[type="checkbox"] { 128 | appearance: none; 129 | display: inline-block; 130 | width: .9rem; 131 | height: .9rem; 132 | padding-left: .1rem; 133 | border: 0; 134 | box-sizing: border-box; 135 | margin: 0; 136 | background: rgba(0,0,0,.2); 137 | color: inherit; 138 | font: inherit; 139 | letter-spacing: inherit; 140 | outline: none; 141 | border-radius: 2px; 142 | box-shadow: 1px 1px 5px black inset; 143 | text-align: right; 144 | overflow: visible; 145 | color: transparent; 146 | font-size: 1.2rem; 147 | line-height: .6rem; 148 | } 149 | 150 | input[type="checkbox"]:checked { 151 | color: white; 152 | text-shadow: inherit; 153 | } 154 | 155 | input[type="checkbox"]::after { 156 | content: '✓'; 157 | } 158 | 159 | .amp { 160 | font: italic 160%/.5 Baskerville, 'Palatino Linotype', 'Goudy Old Style', Constantia, Palatino, serif; 161 | vertical-align: -.1em; 162 | opacity: .7; 163 | } 164 | 165 | @keyframes loader { 166 | from { 167 | background-position: 0 0; 168 | } 169 | 170 | to { 171 | background-position: -700px 0; 172 | } 173 | } 174 | 175 | #loader { 176 | display: none; 177 | position: absolute; 178 | z-index: 100; 179 | top: 50%; 180 | left: 50%; 181 | width: 100px; 182 | padding-top: 100px; 183 | margin-top: -50px; 184 | margin-left: -50px; 185 | background: url(img/loader.png) no-repeat left top; 186 | color: rgba(0,0,0,.5); 187 | font-weight: bold; 188 | text-align: center; 189 | text-shadow: 0 1px white; 190 | 191 | animation: 1s loader infinite steps(7); 192 | } 193 | 194 | body[data-loading] > #loader { 195 | display: block; 196 | } 197 | 198 | .page { 199 | display: block; 200 | position: absolute; 201 | top: 0; 202 | left: 0; 203 | width: 100%; 204 | height: 100%; 205 | border: 0; 206 | outline: none; 207 | resize: none; 208 | background: transparent; 209 | } 210 | 211 | .editor.page { 212 | display: none; 213 | box-sizing: border-box; 214 | height: 100%; 215 | box-sizing: border-box; 216 | overflow: auto; 217 | background: url(img/noise.png), 218 | linear-gradient(left, hsl(24,20%,91%) 1px, transparent 1px) 2.8em 0 no-repeat; 219 | background-color: hsl(24, 20%, 95%); 220 | font: 100%/1.5 Monaco, Consolas, Inconsolata, 'Deja Vu Sans Mono', 'Droid Sans Mono', 'Andale Mono', 'Lucida Console', monospace; 221 | tab-size: 4; 222 | word-wrap: normal; 223 | text-shadow: 0 1px white; 224 | box-shadow: 1px 1px 2px rgba(0,0,0,.3) inset; 225 | } 226 | 227 | .editor.page.focus { 228 | box-shadow: .1em .1em .4em rgba(0,0,0,.5) inset; 229 | } 230 | 231 | body:not([data-view="behind"]) > .editor.page { 232 | box-shadow: 0 .3em 1em -.4em black inset; 233 | } 234 | 235 | body[data-view="behind"] > .editor.page { 236 | background: transparent; 237 | } 238 | 239 | body[data-view="behind"] .editor.page, 240 | body[data-view="behind"] .editor.page > pre, 241 | body[data-view="behind"] .editor.page > pre > span.token { 242 | text-shadow: 0 .1em white, 0 -.1em white, .1em 0 white, -.1em 0 white, 243 | .1em .1em white, -.1em .1em white, .1em -.1em white, -.1em -.1em white; 244 | text-shadow: 0 0 0 .1em white; 245 | } 246 | 247 | .editor.page > pre { 248 | position: relative; 249 | padding: 1em 1.5em 1em 3em; 250 | overflow: visible; 251 | word-wrap: normal; 252 | font: inherit; 253 | outline: none; 254 | } 255 | 256 | #result { 257 | z-index: 0; 258 | } 259 | 260 | body[data-page="css"] #css-container, 261 | body[data-page="html"] #html-container { 262 | display: block; 263 | } 264 | 265 | body[data-view="split-vertical"]:not([data-page="result"]) > .page { 266 | width: 50%; 267 | } 268 | 269 | body[data-view="split-vertical"]:not([data-page="result"]) > #result { 270 | left: 50%; 271 | } 272 | 273 | body[data-view="split"]:not([data-page="result"]) > .page, 274 | body:not([data-view]):not([data-page="result"]) > .page { 275 | height: 50%; 276 | } 277 | 278 | body[data-view="split"]:not([data-page="result"]) > .editor.page, 279 | body:not([data-view]):not([data-page="result"]) > .editor.page { 280 | top: 50%; 281 | } 282 | 283 | body[data-view="separate"]:not([data-page="result"]) #result { 284 | height: 10px; 285 | top: -10px; 286 | } 287 | 288 | .line-highlight { 289 | position: absolute; 290 | left: 0; 291 | right: 0; 292 | padding-left: .6em; 293 | margin-top: 1em; 294 | background: url(img/noise.png), linear-gradient(left, hsla(24, 20%, 50%,.08) 70%, hsla(24, 20%, 50%,0)); 295 | } 296 | 297 | section:not(.focus) > .line-highlight, 298 | body[data-view="behind"] .line-highlight { 299 | display: none; 300 | } 301 | 302 | .line-highlight:before { 303 | content: attr(data-line); 304 | display: inline-block; 305 | min-width: 1em; 306 | padding: 0 .5em; 307 | background: inherit; 308 | background-color: hsla(24, 20%, 50%,.4); 309 | color: hsl(24, 20%, 95%); 310 | font: bold 65%/1.5 sans-serif; 311 | text-align: center; 312 | vertical-align: .3em; 313 | border-radius: 999px; 314 | text-shadow: none; 315 | box-shadow: 0 1px white; 316 | } 317 | 318 | header { 319 | position: absolute; 320 | bottom: 100%; 321 | right: 0; 322 | left: 0; 323 | z-index: 10; 324 | padding: .4em .5em 0; 325 | border-bottom: 10px solid black; 326 | background: url(img/noise.png), linear-gradient(hsl(200, 10%, 20%), hsla(200, 10%, 20%, .9)); 327 | color: white; 328 | line-height: 1.1; 329 | font-weight: bold; 330 | transition: .5s 1s transform; 331 | } 332 | 333 | header:hover, 334 | header:active, 335 | header.focus, 336 | body.tabediting header { 337 | transform: translateY(-10px) translateY(100%); 338 | transition-delay: 0s; 339 | } 340 | 341 | header > h1 { 342 | float: left; 343 | height: 1.3em; 344 | margin-right: .1em; 345 | font: 900 190%/1.45 Dabblet, sans-serif; 346 | text-shadow: .05em .05em .1em black; 347 | -webkit-transform: translateZ(0); /* fix for chopped letters */ 348 | } 349 | 350 | header > .with-dropdown { 351 | float: left; 352 | } 353 | 354 | #view > input[type="radio"], 355 | .tabs input { 356 | position: absolute; 357 | z-index: 1; 358 | opacity: 0; 359 | } 360 | 361 | .tabs { 362 | display: inline-block; 363 | height: .75em; 364 | margin: 0 2px 0 -.5em; 365 | white-space: nowrap; 366 | font-size: 110%; 367 | line-height: 1.4; 368 | -webkit-font-smoothing: antialiased; 369 | -webkit-transform: translateZ(0); /* fix for unhoverable tabs */ 370 | } 371 | 372 | .controls-group { 373 | float: right; 374 | min-width: 15em; 375 | white-space: nowrap; 376 | } 377 | 378 | header .button, 379 | header button, 380 | .tabs > label { 381 | padding: .45em .6em; 382 | border: 0; 383 | border-radius: .3em; 384 | background: inherit; 385 | color: inherit; 386 | font: inherit; 387 | text-align: center; 388 | text-decoration: none; 389 | cursor: pointer; 390 | text-shadow: inherit; 391 | } 392 | 393 | header .button, 394 | header button { 395 | float: left; 396 | border: 1px solid rgba(0,0,0,.4); 397 | margin: 0 .2em; 398 | white-space: nowrap; 399 | background-image: url(img/noise.png); 400 | box-shadow: rgba(255, 255, 255, .4) 0 1px 0 inset, 401 | rgba(255, 255, 255, .3) 0 25px 30px -12px inset, 402 | rgba(0, 0, 0, .6) 0 1px 2px; 403 | text-shadow: 0 -1px 1px black, 0 -1px 2px black; 404 | } 405 | 406 | header button.with-symbol, 407 | header .button.with-symbol { 408 | font-family: Dabblet, sans-serif; 409 | } 410 | 411 | body[data-unsaved] button[title="Save"] { 412 | text-shadow: 0 0 .2em #6cf, 0 0 .4em #6cf; 413 | } 414 | 415 | header > .with-dropdown > button:after, 416 | header > .with-dropdown > .button:after { 417 | content: ' ▾'; 418 | } 419 | 420 | header .button:active, 421 | header button:active, 422 | input:checked + label.button { 423 | box-shadow: 0 2px 6px black inset; 424 | } 425 | 426 | input:checked + label.button { 427 | background-color: rgba(0,0,0,.3); 428 | } 429 | 430 | #currentuser { 431 | max-width: 8em; 432 | overflow: hidden; 433 | text-overflow: ellipsis; 434 | } 435 | 436 | #currentuser:not(:hover) { 437 | background-color: transparent; 438 | } 439 | 440 | .user > img { 441 | max-height: 1.4em; 442 | margin: -.2em .3em -.2em -1.6em; 443 | vertical-align:bottom; 444 | border-radius: 1px; 445 | box-shadow: 0 0 0 1px rgba(0,0,0,.2); 446 | } 447 | 448 | #currentuser > img { 449 | margin-left: -.3em; 450 | } 451 | 452 | a.button[href="/help/"] { 453 | float: right; 454 | background-color: transparent; 455 | } 456 | 457 | #view > input, 458 | .tabs > input { 459 | position: absolute; 460 | clip: rect(0,0,0,0); 461 | } 462 | 463 | @keyframes flip { 464 | from { transform: perspective(600px) rotate3d(1,0,0,180deg); } 465 | to { transform: perspective(600px) rotate3d(1,0,0, 0deg); } 466 | } 467 | 468 | .tabs > label { 469 | position: relative; 470 | display: inline-block; 471 | padding: .5em .6em; 472 | border: 0; 473 | margin: 0 .15em -5px; 474 | background: rgba(0,0,0,.4); 475 | cursor: pointer; 476 | transform-origin: bottom; 477 | border-radius: 5px; 478 | box-shadow: .05em .1em .25em rgba(0,0,0,.7) inset; 479 | text-shadow: inherit; 480 | animation: .5s flip; 481 | } 482 | 483 | .tabs > input:checked + label { 484 | background: black; 485 | top: 2.5em; 486 | animation: .6s flip; 487 | transform-origin: top; 488 | } 489 | 490 | body[data-view="separate"] .tabs > label .if-not-separate { 491 | display: none; 492 | } 493 | 494 | .tabs > label > .title { 495 | overflow: hidden; 496 | transition: 1s max-width; 497 | } 498 | 499 | 500 | header .button:hover, 501 | header .button:active, 502 | header button:hover, 503 | header button:active, 504 | .tabs > label:hover, 505 | .tabs > label:active { 506 | background-color: black; 507 | } 508 | 509 | .dropdown { 510 | display: none; 511 | position: absolute; 512 | z-index: 2; 513 | min-width: 12em; 514 | max-width: 400px; 515 | padding: .3em; 516 | margin: 2.2em 0 0 -1em; 517 | font-family: sans-serif; 518 | text-align: left; 519 | -webkit-transform: translateZ(0); /* fix for unhoverable dropdowns */ 520 | } 521 | 522 | /* pointer */ 523 | .dropdown:before { 524 | content: ''; 525 | position: absolute; 526 | top: -6px; 527 | left: 2.5em; 528 | width: 12px; 529 | height: 12px; 530 | border: inherit; 531 | border-bottom-width:0; 532 | border-right-width:0; 533 | margin-left: -9px; 534 | background: url(img/noise.png), linear-gradient(-45deg, hsl(200, 10%, 20%) 52%, hsla(200, 10%, 20%, 0) 55%); 535 | background-origin: border-box; 536 | transform: rotate(45deg); 537 | box-shadow: 1px 0 rgba(255,255,255,.3) inset; 538 | } 539 | 540 | .with-dropdown { 541 | padding-bottom: 3px; 542 | } 543 | 544 | .with-dropdown:hover > .dropdown, 545 | .with-dropdown.focus > .dropdown, 546 | .with-dropdown:active > .dropdown, 547 | .dropdown:hover, 548 | .dropdown:active, 549 | .dropdown.focus { 550 | display: block; 551 | } 552 | 553 | #view > label { 554 | width: 10em; 555 | margin: .15em; 556 | overflow: hidden; 557 | text-align: left; 558 | line-height: 25px; 559 | } 560 | 561 | #view > label:before { 562 | content: ''; 563 | float: left; 564 | width: 25px; 565 | height: 25px; 566 | margin-right: 10px; 567 | background: url(img/settings.png); 568 | } 569 | 570 | #view > label[for="view-split"]:before { 571 | background-position: 0 -25px; 572 | } 573 | 574 | #view > label[for="view-split-vertical"]:before { 575 | background-position: 0 -50px; 576 | } 577 | 578 | #view > label[for="view-separate"]:before { 579 | background-position: 0 -75px; 580 | } 581 | 582 | fieldset:not([id]) > label { 583 | font-weight: normal; 584 | font-size: 90%; 585 | } 586 | 587 | menu > .command { 588 | display: block; 589 | padding: .4em; 590 | border-radius: .3em; 591 | cursor: pointer; 592 | } 593 | 594 | menu > .command[data-disabled] { 595 | opacity: .5; 596 | cursor: not-allowed; 597 | } 598 | 599 | menu > .command:before { 600 | content: ''; 601 | display: inline-block; 602 | width: 1.4em; 603 | } 604 | 605 | menu > .command[data-keyboard]:after { 606 | content: attr(data-keyboard); 607 | float: right; 608 | opacity: .6; 609 | } 610 | 611 | menu > input:checked + label.command:before { 612 | content: '✔'; 613 | color: hsla(0,0%,100%,.5); 614 | } 615 | 616 | menu > hr { 617 | height: 2px; 618 | border: 0; 619 | margin: .1em 0; 620 | background: rgba(255,255,255,.5); 621 | background: linear-gradient(left, transparent, black 50%, transparent) top, 622 | linear-gradient(left, rgba(255,255,255,0), rgba(255,255,255,.6) 50%, rgba(255,255,255,0)) bottom; 623 | background-repeat: repeat-x; 624 | background-size: 100% 1px; 625 | } 626 | 627 | .popup { 628 | display: none; 629 | position: absolute; 630 | top: 10%; 631 | bottom: 10%; 632 | left: 50%; 633 | z-index: 20; 634 | width: 60%; 635 | padding: .6em .6em 5.2em; 636 | box-sizing: border-box; 637 | margin-left: -30%; 638 | } 639 | 640 | .popup:target { 641 | display: block; 642 | } 643 | 644 | .popup > h1 { 645 | color: white; 646 | font-size: 160%; 647 | } 648 | 649 | .popup > .close { 650 | position: absolute; 651 | top: 10px; 652 | right: 10px; 653 | width: 1.2em; 654 | font-size: 150%; 655 | line-height: 1.2; 656 | font-weight: bold; 657 | text-align: center; 658 | border-radius: 50%; 659 | } 660 | 661 | .popup > .content { 662 | height: 100%; 663 | padding: 1em; 664 | border: inherit; 665 | overflow: auto; 666 | background-clip: padding-box; 667 | border-radius: 3px; 668 | } 669 | 670 | /** 671 | * Code hightlighting 672 | */ 673 | .token { 674 | /* See issue #3 */ 675 | padding: .15em 0; 676 | } 677 | 678 | .comment { 679 | color: slategray; 680 | } 681 | 682 | .property, 683 | .tag, 684 | .popup > .content a { 685 | color: #905; 686 | } 687 | 688 | .selector, 689 | .attr-name, 690 | .popup > .content a:hover { 691 | color: #690; 692 | } 693 | 694 | .atrule, 695 | .attr-value, 696 | .popup > .content a:active { 697 | color: #07a; 698 | } 699 | 700 | .important { 701 | color: #e90; 702 | font-weight: bold; 703 | } 704 | 705 | .color, 706 | .abslength, 707 | .easing, 708 | .time, 709 | .angle, 710 | .fontfamily, 711 | .gradient, 712 | .url, 713 | .entity { 714 | text-shadow: 0 1px white, 0 0 .2em hsla(24, 100%, 50%, .5); 715 | cursor: help; 716 | } 717 | 718 | .token[data-active] { 719 | text-shadow: 0 1px white, 0 0 .2em hsla(24, 100%, 50%, .8); 720 | } 721 | 722 | .punctuation { 723 | color: #999; 724 | } 725 | 726 | 727 | /** 728 | * Cute little value previewers 729 | */ 730 | .previewer { 731 | display: none; 732 | position: absolute; 733 | left: 120%; /* off the page */ 734 | margin-bottom: 10px; 735 | border-radius: 8px; 736 | box-shadow: 1px 1px 8px rgba(0,0,0,.7); 737 | } 738 | 739 | @keyframes previewer { 740 | from { transform: scale(.1); } 741 | 742 | to { transform: scale(1); } 743 | } 744 | 745 | .previewer.active { 746 | display: block; 747 | transform-origin: 50% 100%; 748 | animation: .2s previewer; 749 | animation-timing-function: cubic-bezier(.5,0,.7,1.8); 750 | } 751 | 752 | .previewer.flipped.active { 753 | transform-origin: 50% 0%; 754 | } 755 | 756 | /* pointer */ 757 | .previewer:before { 758 | content: ''; 759 | position: absolute; 760 | bottom: -6px; 761 | left: 50%; 762 | width: 12px; 763 | height: 12px; 764 | border: inherit; 765 | border-top-width:0; 766 | border-left-width:0; 767 | margin-left: -6px; 768 | background: white; 769 | background: linear-gradient(-45deg, transparent 48%, white 48%); 770 | transform: rotate(45deg); 771 | box-shadow: inherit; 772 | } 773 | 774 | .previewer.flipped:before { 775 | top: -6px; 776 | background: linear-gradient(135deg, transparent 48%, white 48%); 777 | } 778 | 779 | .previewer:after, 780 | #gradient > div { 781 | content: ''; 782 | position: absolute; 783 | top: 0; right: 0; bottom: 0; left: 0; 784 | z-index: 2; 785 | border: 5px solid white; 786 | border-radius: inherit; 787 | box-shadow: 1px 1px 5px rgba(0,0,0,.5) inset; 788 | } 789 | 790 | .previewer > img, 791 | .previewer > svg { 792 | display: block; 793 | z-index: 1; 794 | position: relative; 795 | } 796 | 797 | #color, 798 | #gradient, 799 | #url { 800 | background: linear-gradient(45deg, #bbb 25%, transparent 25%, transparent 75%, #bbb 75%, #bbb) 5px 5px, 801 | linear-gradient(45deg, #bbb 25%, #eee 25%, #eee 75%, #bbb 75%, #bbb) 15px 15px; 802 | background-size:20px 20px; 803 | } 804 | 805 | #color { 806 | width: 60px; 807 | height: 60px; 808 | margin-left: -30px; 809 | } 810 | 811 | #color:after { 812 | background-color: inherit; 813 | } 814 | 815 | #gradient { 816 | width: 180px; 817 | height: 100px; 818 | margin-left: -100px; 819 | } 820 | 821 | #gradient:after { 822 | content: none; 823 | } 824 | 825 | #abslength { 826 | max-width: 100%; 827 | height: 20px; 828 | border: 1px solid white; 829 | border-radius: 0; 830 | transition: .3s; 831 | transition-property:width, margin-left; 832 | } 833 | 834 | #abslength:before { 835 | bottom: -7px; 836 | background: url(img/noise.png), 837 | linear-gradient(-45deg, hsla(200, 10%, 20%, 0) 47%, hsl(200, 10%, 20%) 48%); 838 | } 839 | 840 | #abslength[data-size="small"]:before { 841 | width: 6px; 842 | height: 6px; 843 | bottom: -3px; 844 | margin-left: -3px; 845 | } 846 | 847 | #abslength[data-size="tiny"]:before { 848 | transform: none; 849 | width: 0; 850 | margin-left: 0; 851 | } 852 | 853 | #abslength:after { 854 | border: 0; 855 | background: 856 | repeating-linear-gradient(left, transparent, transparent 19px, rgba(255,255,255,.6) 19px, rgba(255,255,255,.6) 20px) left top no-repeat, 857 | repeating-linear-gradient(left, transparent, transparent 4px, rgba(255,255,255,.4) 4px, rgba(255,255,255,.4) 5px) left top no-repeat, 858 | url(img/noise.png), linear-gradient(hsla(200, 10%, 20%, .8), hsl(200, 10%, 20%)); 859 | background-size: 100% 10px, 100% 5px, auto, auto; 860 | box-shadow: none; 861 | } 862 | 863 | #time, 864 | #angle { 865 | width: 74px; 866 | height: 74px; 867 | margin-left: -37px; 868 | background: linear-gradient(right, hsla(24, 20%, 95%, .4), hsl(24, 20%, 95%) 95%); 869 | border-radius: 37px; 870 | } 871 | 872 | #time:before, 873 | #angle:before { 874 | bottom: -4px; 875 | } 876 | 877 | #time:after, 878 | #angle:after { 879 | background: url(img/noise.png); 880 | z-index: 2; 881 | } 882 | 883 | #angle > svg, 884 | #time > svg { 885 | margin: 5px; 886 | width: 64px; 887 | height: 64px; 888 | border-radius: 32px; 889 | background: inherit; 890 | transform: rotate(-90deg); 891 | -webkit-transform: translateZ(0) rotate(-90deg); 892 | } 893 | 894 | #angle[data-negative] > svg { 895 | transform: scaleX(-1) rotate(-90deg); 896 | -webkit-transform: translateZ(0) scaleX(-1) rotate(-90deg); 897 | } 898 | 899 | #time circle, 900 | #angle circle { 901 | stroke: hsl(200, 10%, 20%); 902 | stroke-opacity: .9; 903 | fill: transparent; 904 | stroke-width: 31.8; 905 | } 906 | 907 | #fontfamily { 908 | padding: .5em 0 .3em; 909 | width: 10em; 910 | margin-left: -5em; 911 | color: white; 912 | font-size: 150%; 913 | font-style: italic; 914 | line-height: 1.8; 915 | text-align: center; 916 | white-space: pre-line; 917 | } 918 | 919 | #fontfamily:first-line { 920 | display: block; 921 | font-style: normal; 922 | } 923 | 924 | #easing { 925 | width: 100px; 926 | height: 100px; 927 | padding: 5px; 928 | margin-left: -50px; 929 | } 930 | 931 | 932 | #easing:after { 933 | z-index: 2; 934 | } 935 | 936 | 937 | #easing circle { 938 | stroke: white; 939 | fill: hsl(200, 10%, 20%); 940 | } 941 | 942 | #easing path { 943 | fill: none; 944 | stroke: white; 945 | stroke-width: 4; 946 | stroke-linecap: round; 947 | } 948 | 949 | #easing line { 950 | stroke: white; 951 | stroke-opacity:.5; 952 | stroke-width: 2; 953 | marker: url(#marker); 954 | } 955 | 956 | #url { 957 | padding: 5px; 958 | } 959 | 960 | #url > img { 961 | max-width: 200px; 962 | max-height: 200px; 963 | } 964 | 965 | #url > img.error { 966 | width: 50px; 967 | height: 50px; 968 | } 969 | 970 | #entity { 971 | min-width: 1.6em; 972 | margin-left: -.8em; 973 | font-size: 500%; 974 | color: white; 975 | text-align: center; 976 | text-shadow: .02em .02em .06em black; 977 | } 978 | 979 | /** 980 | * Style scrollbars in Webkit 981 | */ 982 | ::-webkit-scrollbar { 983 | width: 20px; 984 | height: 20px; 985 | } 986 | 987 | section.page:not(.focus):not(:hover)::-webkit-scrollbar { 988 | display: none; 989 | } 990 | 991 | ::-webkit-scrollbar-track, 992 | ::-webkit-scrollbar-thumb { 993 | border-radius: 999px; 994 | border: 5px solid transparent; 995 | } 996 | 997 | ::-webkit-scrollbar-track { 998 | box-shadow: 1px 1px 5px rgba(0,0,0,.2) inset; 999 | } 1000 | 1001 | ::-webkit-scrollbar-thumb { 1002 | min-height: 20px; 1003 | background: url(img/noise.png); 1004 | background-clip: content-box; 1005 | box-shadow: 0 0 0 5px hsla(24, 20%, 50%,.4) inset; 1006 | } 1007 | 1008 | ::-webkit-scrollbar-corner { 1009 | background: transparent; 1010 | } 1011 | 1012 | /** 1013 | * Responsive shit 1014 | */ 1015 | @media (max-width: 400px) { 1016 | html { 1017 | font-size: 90%; 1018 | } 1019 | } 1020 | 1021 | @media (max-width: 620px) { 1022 | header > .with-dropdown button:after { 1023 | content: ''; 1024 | content: none; 1025 | } 1026 | 1027 | .dropdown { 1028 | max-width: 200px; 1029 | margin-left: -10em; 1030 | } 1031 | 1032 | #currentuser { 1033 | max-width: 3em; 1034 | } 1035 | } 1036 | 1037 | @media (max-width: 800px) { 1038 | html { 1039 | font-size: 95%; 1040 | } 1041 | 1042 | .controls-group { 1043 | margin-top: .2em; 1044 | } 1045 | 1046 | .tabs { 1047 | height: 2.2em; 1048 | } 1049 | 1050 | .popup { 1051 | width: 290px; 1052 | padding: .5em .5em 5em; 1053 | margin-left: -145px; 1054 | top: 2%; 1055 | bottom: 2%; 1056 | } 1057 | } 1058 | 1059 | @media (min-width: 1000px) { 1060 | .popup { 1061 | width: 800px; 1062 | margin-left: -400px; 1063 | } 1064 | 1065 | .popup > .content > div { 1066 | column-count: 2; 1067 | column-gap: 2em; 1068 | column-rule: 3px dotted slategray; 1069 | column-fill: balance; 1070 | } 1071 | } 1072 | 1073 | ::selection { 1074 | background-color: initial; /* see issue #140 */ 1075 | color: white; 1076 | text-shadow: none; 1077 | } 1078 | --------------------------------------------------------------------------------