1
0
mirror of https://github.com/chinchang/web-maker.git synced 2025-05-16 07:09:40 +02:00

Merge pull request #25 from chinchang/release-2.0

Release 2.0
This commit is contained in:
Kushagra Gour 2017-01-01 17:54:46 +05:30 committed by GitHub
commit e968229ee1
15 changed files with 8292 additions and 587 deletions

View File

@ -2,6 +2,7 @@
"env": {
"browser": true
},
"parser": "babel-eslint",
"extends": "eslint:recommended",
"rules": {
"accessor-pairs": "error",
@ -77,9 +78,9 @@
"newline-after-var": "off",
"newline-before-return": "off",
"newline-per-chained-call": "error",
"no-alert": "error",
"no-alert": "off",
"no-array-constructor": "error",
"no-bitwise": "error",
"no-bitwise": "off",
"no-caller": "error",
"no-console": "off",
"no-catch-shadow": "error",
@ -96,7 +97,7 @@
"no-extra-label": "error",
"no-extra-parens": "off",
"no-floating-decimal": "error",
"no-implicit-coercion": "error",
"no-implicit-coercion": "off",
"no-implicit-globals": "off",
"no-implied-eval": "error",
"no-inline-comments": "off",
@ -164,7 +165,7 @@
"no-unneeded-ternary": "error",
"no-unsafe-finally": "error",
"no-unused-expressions": "error",
"no-use-before-define": "error",
"no-use-before-define": "off",
"no-useless-call": "error",
"no-useless-computed-key": "error",
"no-useless-concat": "off",
@ -173,7 +174,7 @@
"no-useless-rename": "error",
"no-var": "off",
"no-void": "error",
"no-warning-comments": "error",
"no-warning-comments": "off",
"no-whitespace-before-property": "error",
"no-with": "error",
"object-curly-spacing": [
@ -259,6 +260,10 @@
"marked": true,
"jade": true,
"loadJS": true,
"Promise": true
"esprima": true,
"escodegen": true,
"utils": true,
"Promise": true,
"Inlet": true
}
}
}

View File

@ -1,7 +1,8 @@
language: node_js
node_js:
- '4.0'
- '6.0'
install:
- npm install -g eslint
- npm install -g babel-eslint
script:
- eslint src/*.js

View File

@ -1,16 +1,20 @@
/* global ga */
// eslint-disable-next-line max-params
window.trackEvent = function (category, action, label, value) {
if (window.DEBUG) {
utils.log('trackevent', category, action, label, value)
return;
}
if (window.ga) {
ga('send', 'event', category, action, label, value);
}
}
// if online, load after sometime
if (navigator.onLine) {
setTimeout(function() {
// if online, load after 2 seconds
if (navigator.onLine && !window.DEBUG) {
/* eslint-disable */
/* eslint-disable */
setTimeout(function() {
(function(i,s,o,g,r,a,m){
i['GoogleAnalyticsObject']=r;
i[r]=i[r]||function(){
@ -22,8 +26,7 @@ if (navigator.onLine) {
// required for chrome extension protocol
ga('set', 'checkProtocolTask', function(){ /* nothing */ });
ga('send', 'pageview');
/* eslint-enable */
}, 0);
/* eslint-enable */
}

View File

@ -1,8 +1,12 @@
chrome.browserAction.onClicked.addListener(function(){
function openApp() {
chrome.tabs.create({
url: chrome.extension.getURL('index.html'),
selected: true
});
}
chrome.browserAction.onClicked.addListener(function(){
openApp();
});
// Listen for tabs getting created.
@ -22,4 +26,15 @@ chrome.tabs.onCreated.addListener(function (tab) {
}
});
}
});
chrome.runtime.onInstalled.addListener(function callback (details) {
if (details.reason === 'install') {
openApp();
}
if (details.reason === 'update') {
if ((details.previousVersion + '').indexOf('1.') === 0) {
openApp();
}
}
});

View File

@ -5,396 +5,83 @@
<link rel="stylesheet" href="lib/codemirror/lib/codemirror.css">
<link rel="stylesheet" href="lib/codemirror/theme/monokai.css">
<link rel="stylesheet" href="lib/hint.min.css">
<style>
@font-face {
font-family: 'Inconsolata';
font-style: normal;
font-weight: 400;
src: local('Inconsolata'), url(../Inconsolata-Regular.ttf) format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
body {
margin: 0;
padding: 0;
background: rgba(0, 0, 0, 0.5);
background: #444;
min-height: 100vh;
font-family: Helvetica, arial;
}
h1 {
margin-top: 0;
}
a { text-decoration: none; color: crimson; cursor: pointer; }
/*a:hover { text-decoration: underline; }*/
.flex {
display: flex;
}
.flex-grow {
flex-grow: 1;
}
.fr {
float: right;
}
.caret {
display: inline-block;
width: 0;
height: 0;
border: 6px solid transparent;
border-top-color: currentColor;
position: relative;
top: 5px;
margin-left: 7px;
}
.btn {
display: inline-block;
border: 0;
background: #0074d9;
/*border: 1px solid #aaa;*/
color: white;
font-size: inherit;
border-radius: 3px;
padding: 7px 15px;
cursor: pointer;
transition: 0.2s ease;
}
.btn:hover {
text-decoration: none;
box-shadow: 0 3px 5px 0 rgba(0,0,0,0.15);
}
.star:after {
content: '★';
color: #eee333;
}
.main-container {
position: absolute;
left: 0; right: 0;
top: 0; bottom: 0;
display: flex;
flex-direction: column;
}
.code-side,
.demo-side {
flex-basis: inherit;
position: relative;
}
.layout-3 .content-wrap {
flex-direction: row-reverse;
}
.code-side {
display: flex;
flex-direction: column;
}
.layout-2 .content-wrap {
flex-direction: column;
}
.layout-2 .code-side {
flex-direction: row;
}
.code-wrap {
flex-basis: inherit;
height: 33%;
overflow: hidden;
position: relative;
/*animation: pop-in 0.4s cubic-bezier(.71,1.7,.77,1.24) forwards 0.2s;*/
/*animation: pop-in 0.4s ease forwards 0.2s;*/
/*opacity: 0;*/
}
.layout-2 .code-wrap {
height: auto;
width: 33%;
}
.code-wrap:nth-of-type(3) {
animation-delay: 0.3s;
}
.code-wrap:nth-of-type(5) {
animation-delay: 0.4s;
}
.code-wrap__header {
padding: 5px 20px;
background: rgba(0,0,0,0.55);
color: #888;
border-bottom: 1px solid rgba(0,0,0,0.3);
font-weight: bold;
}
@keyframes pop-in {
from { transform: scale(0.9); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
/* Codemirror */
.Codemirror {
width: 100%;
height: calc(100% - 25px); /* 25px for header */
font-size: 16px;
}
.Codemirror pre {
font-family: 'Inconsolata', monospace;
}
.cm-s-monokai .CodeMirror-linenumber {
color:rgba(255,255,255,0.2);
}
#demo-frame {
border: 0;
width: 100%;
height: 100%;
position: absolute;
z-index: 1;
background: white;
}
.footer {
padding: 5px 10px;
background-color: #111;
color: rgba(255, 255, 255, 0.45);
border-top: 1px solid rgba(255,255,255,0.14);
line-height: 20px;
}
.logo {
display: inline-block;
height: 25px;
width: 48px;
margin-right: 5px;
background: url(icon-48.png) 0px -12px;
background-repeat: no-repeat;
vertical-align: middle;
-webkit-filter: grayscale(0.9);
transition: 0.4s ease;
}
.footer:hover .logo {
-webkit-filter: grayscale(0);
}
.footer__right {
font-size: 0;
line-height: 0;
}
.footer__separator {
display: inline-block;
height: 24px;
margin: 0 10px 0 20px;
border-left: 1px solid rgba(255,255,255,0.2);
}
.mode-btn {
margin-left: 10px;
display: inline-block;
}
.footer__link:first-of-type {
margin-left: 5px;
}
.footer__link {
display: inline-block;
margin-right: 5px;
position: relative;
top: 2px;
}
.footer a > svg {
transition: 0.3s ease;
fill: rgba(255, 255, 255, 0.2)
}
.footer a:hover svg {
fill: rgba(255, 255, 255, 0.45)
}
.mode-btn svg {
width: 24px;
height: 24px;
}
.mode-btn.selected svg {
fill: rgba(255, 255, 255, 0.45);
}
.gutter {
background: rgba(0,0,0,0.2);
}
.gutter-horizontal {
cursor: ew-resize;
}
.gutter-vertical {
cursor: ns-resize;
}
.modal {
position: fixed;
top: 5vh;
left: 50%;
width: 70vw;
margin-left: -35vw;
max-width: 90vw;
height: auto;
z-index: 2000;
visibility: hidden;
}
.modal__content {
background: #fdfdfd;
position: relative;
border-radius: 3px;
margin: 0 auto;
opacity: 0;
padding: 2em;
font-size: 1.5em;
transition: all 0.3s;
transform: scale(0.7);
}
.is-modal-visible {
visibility: visible;
}
.is-modal-visible .modal__content {
transform: scale(1);
opacity: 1;
}
.modal-overlay {
position: fixed;
width: 100%;
height: 100%;
visibility: hidden;
top: 0;
left: 0;
z-index: 1000;
opacity: 0;
background: rgba(0,0,0,0.6);
transition: all 0.3s;
}
.is-modal-visible ~ .modal-overlay {
opacity: 1;
visibility: visible;
}
.notifications-btn {
position: relative;
}
@keyframes shake {
2%, 22% {
transform: translate3d(-1px, 0, 0);
}
5%,20% {
transform: translate3d(2px, 0, 0);
}
7%, 12%, 17% {
transform: translate3d(-4px, 0, 0);
}
10%, 15% {
transform: translate3d(4px, 0, 0);
}
}
.notifications-btn.has-new {
animation: shake 10s linear infinite;
transform-origin: 50% 10px;
}
.notifications-btn__dot {
position: absolute;
right: 1;
top: -2px;
background: #2fbe3d;
border-radius: 50%;
width: 12px;
height: 12px;
display: none;
}
.has-new .notifications-btn__dot {
display: block;
}
.notification {
border: 1px solid #f1f1f1;
border-radius: 5px;
padding: 20px;
background: #f8f6f9;
position: relative;
}
.notification:not(:last-child) {
margin-bottom: 10px;
}
.notification li:not(:last-child) {
margin-bottom: 10px;
}
.notification__version {
background: #ff8c00;
color: white;
padding: 3px;
border-radius: 5px;
position: absolute;
top: 2px;
left: 2px;
}
.btn-group {
position: relative;
cursor: pointer;
}
.dropdown__menu {
position: absolute;
top: 100%;
left: 0;
padding: 0;
margin: 0;
min-width: 200px;
display: block;
list-style: none;
border-radius: 4px;
overflow: hidden;
opacity: 0;
visibility: hidden;
transition: 0.25s ease;
transform: translateY(10px);
z-index: 5;
background: white;
}
.dropdown__menu > li > a {
display: block;
padding: 15px;
color: #333;
cursor: pointer;
}
.dropdown__menu > li > a:hover {
background: #ff8c00;
color: white;
}
.dropdown__menu > li:not(:last-child) {
border-bottom: 1px solid rgba(0,0,0,0.05);
}
.open .dropdown__menu {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
</style>
<link rel="stylesheet" href="lib/inlet.css">
<link rel="stylesheet" href="style.css">
</head>
<body class="layout-">
<body>
<div class="main-container">
<div class="main-header">
<div class="main-header__btn-wrap fr flex flex-v-center">
<a id="js-add-library-btn" class="fleex-v-center hint--bottom-left" aria-label="Add a JS/CSS library">
Add library <span id="js-external-lib-count" style="display:none;" class="count-label"></span>
</a>
<a id="newBtn" class="flex flex-v-center hint--bottom-left" aria-label="Start a new creation">
<svg style="vertical-align:middle;width:14px;height:14px" viewBox="0 0 24 24">
<path d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" />
</svg>New
</a>
<a id="saveBtn" class="flex flex-v-center hint--bottom-left" aria-label="Save current creation (Ctrl/⌘ + S)">
<svg style="vertical-align:middle;width:14px;height:14px" viewBox="0 0 24 24">
<path d="M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z" />
</svg>Save</a>
<a id="openBtn" class="flex flex-v-center hint--bottom-left" aria-label="Open a saved creation (Ctrl/⌘ + O)">
<svg style="width:14px;height:14px;vertical-align:middle;" viewBox="0 0 24 24">
<path d="M13,9V3.5L18.5,9M6,2C4.89,2 4,2.89 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2H6Z" />
</svg>Open
</a>
</div>
<input type="text" id="js-title-input" title="Click to edit" class="item-title-input" value="Untitled Work">
</div>
<div class="content-wrap flex flex-grow">
<div class="code-side" id="js-code-side">
<div id="js-html-code" data-type="html" class="code-wrap">
<div class="code-wrap__header">
<div data-code-wrap-id="0" id="js-html-code" data-type="html" class="code-wrap">
<div class="js-code-wrap__header code-wrap__header" title="Double click to toggle code pane">
<div class="btn-group" dropdown title="Click to change">
<span id="js-html-mode-label">HTML</span><span class="caret"></span>
<span id="js-html-mode-label" class="code-wrap__header-label">HTML</span><span class="caret"></span>
<ul class="js-modes-menu dropdown__menu">
<li><a data-type="html" data-mode="html">HTML</a></li>
<li><a data-type="html" data-mode="markdown">Markdown</a></li>
<li><a data-type="html" data-mode="jade">Jade</a></li>
</ul>
</div>
<div class="code-wrap__header-right-options">
<a class="js-code-collapse-btn code-wrap__header-btn code-wrap__collapse-btn" title="Toggle code pane">
</a>
</div>
</div>
</div>
<div id="js-css-code" data-type="css" class="code-wrap">
<div class="code-wrap__header">
<div data-code-wrap-id="1" id="js-css-code" data-type="css" class="code-wrap">
<div class="js-code-wrap__header code-wrap__header" title="Double click to toggle code pane">
<div class="btn-group" dropdown title="Click to change">
<span id="js-css-mode-label">CSS</span><span class="caret"></span>
<span id="js-css-mode-label" class="code-wrap__header-label">CSS</span><span class="caret"></span>
<ul class="js-modes-menu dropdown__menu">
<li><a data-type="css" data-mode="css">CSS</a></li>
<li><a data-type="css" data-mode="scss">SCSS</a></li>
<li><a data-type="css" data-mode="less">LESS</a></li>
</ul>
</div>
<div class="code-wrap__header-right-options">
<a class="js-code-collapse-btn code-wrap__header-btn code-wrap__collapse-btn" title="Toggle code pane">
</a>
</div>
</div>
</div>
<div id="js-js-code" data-type="js" class="code-wrap">
<div class="code-wrap__header">
<div data-code-wrap-id="2" id="js-js-code" data-type="js" class="code-wrap">
<div class="js-code-wrap__header code-wrap__header" title="Double click to toggle code pane">
<div class="btn-group" dropdown title="Click to change">
<span id="js-js-mode-label">JS</span><span class="caret"></span>
<span id="js-js-mode-label" class="code-wrap__header-label">JS</span><span class="caret"></span>
<ul class="js-modes-menu dropdown__menu">
<li><a data-type="js" data-mode="js">JS</a></li>
<li><a data-type="js" data-mode="coffee">CoffeeScript</a></li>
<li><a data-type="js" data-mode="es6">ES6 (Babel)</a></li>
</ul>
</div>
<div class="code-wrap__header-right-options">
<a class="js-code-collapse-btn code-wrap__header-btn code-wrap__collapse-btn" title="Toggle code pane">
</a>
</div>
</div>
</div>
</div>
@ -404,7 +91,7 @@
</div>
<div class="footer">
<div class="footer__right fr">
<a id="js-save-html" class="mode-btn hint--rounded hint--top-left" data-hint="Save as HTML file">
<a id="saveHtmlBtn" class="mode-btn hint--rounded hint--top-left" data-hint="Save as HTML file">
<svg viewBox="0 0 24 24">
<path d="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z" />
</svg>
@ -413,7 +100,7 @@
<symbol id="codepen-logo" viewBox="0 0 120 120"><path class="outer-ring" d="M60.048 0C26.884 0 0 26.9 0 60.048s26.884 60 60 60.047c33.163 0 60.047-26.883 60.047-60.047 S93.211 0 60 0z M60.048 110.233c-27.673 0-50.186-22.514-50.186-50.186S32.375 9.9 60 9.9 c27.672 0 50.2 22.5 50.2 50.186S87.72 110.2 60 110.233z"/><path class="inner-box" d="M97.147 48.319c-0.007-0.047-0.019-0.092-0.026-0.139c-0.016-0.09-0.032-0.18-0.056-0.268 c-0.014-0.053-0.033-0.104-0.05-0.154c-0.025-0.078-0.051-0.156-0.082-0.232c-0.021-0.053-0.047-0.105-0.071-0.156 c-0.033-0.072-0.068-0.143-0.108-0.211c-0.029-0.051-0.061-0.1-0.091-0.148c-0.043-0.066-0.087-0.131-0.135-0.193 c-0.035-0.047-0.072-0.094-0.109-0.139c-0.051-0.059-0.104-0.117-0.159-0.172c-0.042-0.043-0.083-0.086-0.127-0.125 c-0.059-0.053-0.119-0.104-0.181-0.152c-0.048-0.037-0.095-0.074-0.145-0.109c-0.019-0.012-0.035-0.027-0.053-0.039L61.817 23.5 c-1.072-0.715-2.468-0.715-3.54 0L24.34 46.081c-0.018 0.012-0.034 0.027-0.053 0.039c-0.05 0.035-0.097 0.072-0.144 0.1 c-0.062 0.049-0.123 0.1-0.181 0.152c-0.045 0.039-0.086 0.082-0.128 0.125c-0.056 0.055-0.108 0.113-0.158 0.2 c-0.038 0.045-0.075 0.092-0.11 0.139c-0.047 0.062-0.092 0.127-0.134 0.193c-0.032 0.049-0.062 0.098-0.092 0.1 c-0.039 0.068-0.074 0.139-0.108 0.211c-0.024 0.051-0.05 0.104-0.071 0.156c-0.031 0.076-0.057 0.154-0.082 0.2 c-0.017 0.051-0.035 0.102-0.05 0.154c-0.023 0.088-0.039 0.178-0.056 0.268c-0.008 0.047-0.02 0.092-0.025 0.1 c-0.019 0.137-0.029 0.275-0.029 0.416V71.36c0 0.1 0 0.3 0 0.418c0.006 0 0 0.1 0 0.1 c0.017 0.1 0 0.2 0.1 0.268c0.015 0.1 0 0.1 0.1 0.154c0.025 0.1 0.1 0.2 0.1 0.2 c0.021 0.1 0 0.1 0.1 0.154c0.034 0.1 0.1 0.1 0.1 0.213c0.029 0 0.1 0.1 0.1 0.1 c0.042 0.1 0.1 0.1 0.1 0.193c0.035 0 0.1 0.1 0.1 0.139c0.05 0.1 0.1 0.1 0.2 0.2 c0.042 0 0.1 0.1 0.1 0.125c0.058 0.1 0.1 0.1 0.2 0.152c0.047 0 0.1 0.1 0.1 0.1 c0.019 0 0 0 0.1 0.039L58.277 96.64c0.536 0.4 1.2 0.5 1.8 0.537c0.616 0 1.233-0.18 1.77-0.537 l33.938-22.625c0.018-0.012 0.034-0.027 0.053-0.039c0.05-0.035 0.097-0.072 0.145-0.109c0.062-0.049 0.122-0.1 0.181-0.152 c0.044-0.039 0.085-0.082 0.127-0.125c0.056-0.055 0.108-0.113 0.159-0.172c0.037-0.045 0.074-0.09 0.109-0.139 c0.048-0.062 0.092-0.127 0.135-0.193c0.03-0.049 0.062-0.098 0.091-0.146c0.04-0.07 0.075-0.141 0.108-0.213 c0.024-0.051 0.05-0.102 0.071-0.154c0.031-0.078 0.057-0.156 0.082-0.234c0.017-0.051 0.036-0.102 0.05-0.154 c0.023-0.088 0.04-0.178 0.056-0.268c0.008-0.045 0.02-0.092 0.026-0.137c0.018-0.139 0.028-0.277 0.028-0.418V48.735 C97.176 48.6 97.2 48.5 97.1 48.319z M63.238 32.073l25.001 16.666L77.072 56.21l-13.834-9.254V32.073z M56.856 32.1 v14.883L43.023 56.21l-11.168-7.471L56.856 32.073z M29.301 54.708l7.983 5.34l-7.983 5.34V54.708z M56.856 88.022L31.855 71.4 l11.168-7.469l13.833 9.252V88.022z M60.048 67.597l-11.286-7.549l11.286-7.549l11.285 7.549L60.048 67.597z M63.238 88.022V73.14 l13.834-9.252l11.167 7.469L63.238 88.022z M90.794 65.388l-7.982-5.34l7.982-5.34V65.388z"/></symbol>
</svg>
<a href="" id="js-codepen-btn" class="mode-btn hint--rounded hint--top-left" data-hint="Edit on CodePen">
<a href="" id="codepenBtn" class="mode-btn hint--rounded hint--top-left" data-hint="Edit on CodePen">
<svg>
<use xlink:href="#codepen-logo"></use>
</svg>
@ -421,17 +108,17 @@
<div class="footer__separator"></div>
<a id="js-layout-btn-1" class="mode-btn">
<a id="layoutBtn1" class="mode-btn">
<svg viewBox="0 0 100 100" style="transform:rotate(-90deg)">
<use xlink:href="#mode-icon" />
</svg>
</a>
<a id="js-layout-btn-2" class="mode-btn">
<a id="layoutBtn2" class="mode-btn">
<svg viewBox="0 0 100 100">
<use xlink:href="#mode-icon" />
</svg>
</a>
<a id="js-layout-btn-3" class="mode-btn">
<a id="layoutBtn3" class="mode-btn">
<svg viewBox="0 0 100 100" style="transform:rotate(90deg)">
<use xlink:href="#mode-icon" />
</svg>
@ -439,13 +126,13 @@
<div class="footer__separator"></div>
<a id="js-notifications-btn" class="notifications-btn mode-btn hint--top-left hint--rounded" aria-label="Notifications">
<a id="notificationsBtn" class="notifications-btn mode-btn hint--top-left hint--rounded" aria-label="Notifications">
<svg viewBox="0 0 24 24">
<path d="M14,20A2,2 0 0,1 12,22A2,2 0 0,1 10,20H14M12,2A1,1 0 0,1 13,3V4.08C15.84,4.56 18,7.03 18,10V16L21,19H3L6,16V10C6,7.03 8.16,4.56 11,4.08V3A1,1 0 0,1 12,2Z" />
</svg>
<span class="notifications-btn__dot"></span>
</a>
<a id="js-settings-btn" class="mode-btn hint--top-left hint--rounded" aria-label="Settings">
<a id="settingsBtn" class="mode-btn hint--top-left hint--rounded" aria-label="Settings">
<svg>
<path id="settings-icon" fill="" d="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z"></path>
</svg>
@ -455,7 +142,7 @@
<a href="http://kushagragour.in/lab/web-maker/" target="_blank"><div class="logo"></div></a>
&copy; Web Maker &nbsp;&nbsp;
<a id="js-help-btn" class="footer__link hint--rounded hint--top-right" data-hint="Help">
<a id="helpBtn" class="footer__link hint--rounded hint--top-right" data-hint="Help">
<svg style="width:20px; height:20px; vertical-align:text-bottom" viewBox="0 0 24 24">
<path d="M11,18H13V16H11V18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,6A4,4 0 0,0 8,10H10A2,2 0 0,1 12,8A2,2 0 0,1 14,10C14,12 11,11.75 11,15H13C13,12.75 16,12.5 16,10A4,4 0 0,0 12,6Z" />
</svg>
@ -471,9 +158,33 @@
</div>
</div>
<div class="modal" id="js-help-modal">
<div class="modal" id="addLibraryModal">
<div class="modal__content" id="app">
<h1>Add Library</h1>
<h3>JavaScript</h3>
<textarea id="js-external-js" class="full-width" id="" cols="30" rows="5" placeholder="Put each library in new line"></textarea>
<h3>CSS</h3>
<textarea id="js-external-css" class="full-width" id="" cols="30" rows="5" placeholder="Put each library in new line"></textarea>
<div style="margin-top:20px;">
Choose from popular libraries:
<select name="" id="js-add-library-select">
<option value="">-------</option>
<optgroup label="JavaScript Libraries">
</optgroup>
<optgroup label="CSS Libraries">
</optgroup>
</select>
</div>
</div>
</div>
<div class="modal" id="helpModal">
<div class="modal__content">
<h1>Web Maker<small style="font-size:14px;"> v1.7.1</small></h1>
<h1>Web Maker<small style="font-size:14px;"> v2.0.0</small></h1>
<div>
<p>Made by <a href="https://twitter.com/chinchang457" target="_blank">Kushagra Gour</a></p>
<p>Tweet out your feature requests, comments & suggestions to <a href="https://twitter.com/chinchang457">@chinchang457</a>.</p>
@ -487,6 +198,8 @@
<li><a target="_blank" href="https://nathancahill.github.io/Split.js/">Split.js</a> - Nathan Cahill</li>
<li><a target="_blank" href="https://codemirror.net/">Codemirror</a> - Marijn Haverbeke</li>
<li><a target="_blank" href="https://emmet.io/">Emmet</a> - Sergey Chikuyonok</li>
<li><a target="_blank" href="http://esprima.org/">Esprima</a> - Ariya Hidayat</li>
<li><a target="_blank" href="https://github.com/estools/escodegen">Escodegen</a> - Mozilla</li>
<li><a target="_blank" href="https://kushagragour.in/lab/web-maker">Web Maker!</a> - whhat!</li>
</ul>
</p>
@ -499,15 +212,60 @@
</div>
</div>
<div class="modal" id="js-notifications-modal">
<div class="modal" id="onboardModal">
<div class="modal__content">
<div class="tac">
<svg width="186px" height="87.5px" viewBox="-145 -2 372 175" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="logo-w/o-text" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(-145.000000, -1.000000)">
<polygon id="Path-1" fill="#FF4600" points="31 0 232 0 132 173.310547"></polygon>
<polygon id="Path-1" fill="#FF6C00" points="0 0 201 0 101 173.310547"></polygon>
<polygon id="Path-1" fill="#FF6C00" transform="translate(271.500000, 86.500000) scale(1, -1) translate(-271.500000, -86.500000) " points="171 0 372 0 272 173.310547"></polygon>
<polygon id="Path-1" fill="#FF4600" transform="translate(241.500000, 86.500000) scale(1, -1) translate(-241.500000, -86.500000) " points="141 0 342 0 242 173.310547"></polygon>
</g>
</svg>
<h1 style="margin-top:20px">Welcome to Web Maker</h1>
</div>
<div class="tac">
<p>Web Maker lets you do web experiments quickly and even when you are offline.</p>
<p>
By default, Web Maker shows up in every new tab you open. But you can change that setting:
<a d-click="onModalSettingsLinkClick">Click here to change setting</a>.
</p>
<p>
You can always open this app by clicking the Web Maker icon in the top-right side of your browser.
</p>
<p class="tac">
<button class="btn" d-click="closeAllOverlays">Lets start!</button>
</p>
</div>
</div>
</div>
<div class="modal" id="notificationsModal">
<div class="modal__content">
<h1>Whats new?</h1>
<div class="notification">
<span class="notification__version">2.0.0</span>
<ul>
<li><strong>Save and Load</strong> - Long pending and super-useful, now you can save your creations and resume them anytime later.</li>
<li><strong>Insert JS & CSS</strong> - Load popular JavaScript & CSS libraries in your work without writing any code.</li>
<li><strong>Collapsed Panes</strong> - Collapse/uncollapse code panes with a single click. Your pane configuration is even saved with every creation!</li>
<li><strong>Quick color & number change</strong> - Click on any color or number and experiment with quick values using a slider.</li>
<li><strong>Linting</strong> - See your code errors right where you are coding.</li>
<li>No more browser hang due to infinite loops!</li>
<li><a href="https://kushagragour.in/blog/web-maker-2">Read more about this big release</a></li>
<li>Like it? <a href="https://chrome.google.com/webstore/detail/web-maker/lkfkkhfhhdkiemehlpkgjeojomhpccnh/reviews" target="_blank" class="btn">Please rate Web Maker <span class="star"></span></a></li>
</ul>
</div>
<div class="notification">
<span class="notification__version">1.7.0</span>
<ul>
<li><strong>Preprocessors!</strong> - Enjoy a whole list of preprocessors for HTML(Jade & markdown), CSS(SCSS & LESS) and JavaScript(CoffeeScript & Babel).</li>
<li>More awesome font for code.</li>
<li>Like it? <a href="https://chrome.google.com/webstore/detail/web-maker/lkfkkhfhhdkiemehlpkgjeojomhpccnh/reviews" target="_blank" class="btn">Please rate Web Maker <span class="star"></span></a></li>
</ul>
</div>
<div class="notification">
@ -521,6 +279,14 @@
</div>
</div>
<div id="js-saved-items-pane" class="saved-items-pane">
<button class="btn saved-items-pane__close-btn" id="js-saved-items-pane-close-btn">X</button>
<h3>My Library</h3>
<div id="js-saved-items-wrap" class="saved-items-pane__container">
</div>
</div>
<div class="modal-overlay"></div>
<svg width="30" height="30" viewBox="0 0 100 100" fill="rgba(255, 255, 255, 0.09)">
@ -534,6 +300,7 @@
</defs>
</svg>
<div class="alerts-container" id="js-alerts-container"></div>
<form style="display:none;" action="http://codepen.io/pen/define" method="POST" target="_blank" id="js-codepen-form">
<input type="hidden" name="data" value='{"title": "New Pen!", "html": "<div>Hello, World!</div>"}'>
</form>
@ -554,10 +321,16 @@
<script src="lib/emmet.js"></script>
<script src="lib/split.js"></script>
<script src="lib/inlet.min.js"></script>
<script src="lib/esprima.js"></script>
<script src="lib/escodegen.browser.min.js"></script>
<script src="utils.js"></script>
<script src="analytics.js"></script>
<script src="deferred.js"></script>
<script src="loader.js"></script>
<script src="notifications.js"></script>
<script src="library-list.js"></script>
<script src="script.js"></script>
<script src="dropdown.js"></script>
</body>

1
src/lib/escodegen.browser.min.js vendored Normal file

File diff suppressed because one or more lines are too long

6346
src/lib/esprima.js Normal file

File diff suppressed because one or more lines are too long

155
src/lib/inlet.css Normal file
View File

@ -0,0 +1,155 @@
/*
* =========================================================
* =========================================================
* ColorPicker
* =========================================================
* =========================================================
*
*/
.inlet_clicker {
z-index: 10;
}
.inlet_slider {
opacity: 0.85;
z-index: 10;
width: 24%;
display: block;
border-radius: 3px;
height: 14px;
box-shadow: inset 0px 0px 5px 0px rgba(4, 4, 4, 0.5);
background-color: #d6d6d6;
background-image: linear-gradient(top, #d6d6d6, #ebebeb);
}
.inlet_slider:hover {
opacity: 0.98;
}
.inlet_slider .range {
width: 100%;
height: 100%;
outline: none;
margin-top: 0px;
margin-left: 0px;
border-radius: 3px;
}
.inlet_slider input[type="range"] {
-webkit-appearance: none;
}
@-moz-document url-prefix() {
.inlet_slider input[type="range"] {
position: absolute;
}
}
.inlet_slider input::-moz-range-track {
background: none;
border: none;
outline: none;
}
.inlet_slider input::-webkit-slider-thumb {
cursor: col-resize;
-webkit-appearance: none;
width: 12px;
height: 12px;
border-radius: 6px;
border: 1px solid black;
background-color: red;
box-shadow: 0px 0px 3px 0px rgba(4, 4, 4, 0.4);
background-color: #424242;
background-color: crimson;
background-image: linear-gradient(top, #424242, #212121);
}
/*
* =========================================================
* =========================================================
* ColorPicker
* =========================================================
* =========================================================
*
*/
.ColorPicker {
/*
border: 1px solid rgba(0,0,0,0.5);
border-radius: 6px;
background: #0d0d0d;
background: -webkit-gradient(linear, left top, left bottom, from(#333), color-stop(0.1, #111), to(#000000));
box-shadow: 2px 2px 5px 2px rgba(0,0,0,0.35);
color:#AAA;
*/
text-shadow: 1px 1px 1px #000;
color: #050505;
cursor: default;
display: block;
font-family: 'arial', helvetica, sans-serif;
font-size: 20px;
padding: 7px 8px 20px;
position: absolute;
top: 100px;
left: 700px;
width: 229px;
z-index: 100;
border-radius: 3px;
-webkit-box-shadow: inset 0px 0px 5px 0px rgba(4, 4, 4, 0.5);
box-shadow: inset 0px 0px 5px 0px rgba(4, 4, 4, 0.5);
background-color: rgba(214, 214, 215, 0.85);
/*
background-image: linear-gradient(top, rgb(214, 214, 214), rgb(235, 235, 235));
*/
}
.ColorPicker br {
clear: both;
margin: 0;
padding: 0;
}
.ColorPicker input.hexInput:hover,
.ColorPicker input.hexInput:focus {
color: #aa1212;
}
.ColorPicker input.hexInput {
-webkit-transition-property: color;
-webkit-transition-duration: .5s;
background: none;
border: 0;
margin: 0;
font-family: courier,monospace;
font-size: 20px;
position: relative;
top: -2px;
float: left;
color: #050505;
cursor: text;
}
.ColorPicker div.hexBox {
border: 1px solid rgba(255, 255, 255, 0.5);
border-radius: 2px;
background: #FFF;
float: left;
font-size: 1px;
height: 20px;
margin: 0 5px 0 2px;
width: 20px;
cursor: pointer;
}
.ColorPicker div.hexBox div {
width: inherit;
height: inherit;
}
.ColorPicker div.hexClose {
display: none;
/*
-webkit-transition-property: color, text-shadow;
-webkit-transition-duration: .5s;
position: relative;
top: -1px;
left: -1px;
color:#FFF;
cursor:pointer;
float:right;
padding: 0 5px;
margin:0 4px 3px;
user-select:none;
-webkit-user-select: none;
*/
}
.ColorPicker div.hexClose:hover {
text-shadow: 0 0 20px #fff;
color: #FF1100;
}

2
src/lib/inlet.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,15 +1,39 @@
// The programming goals of Split.js are to deliver readable, understandable and
// maintainable code, while at the same time manually optimizing for tiny minified file size,
// browser compatibility without additional requirements, graceful fallback (IE8 is supported)
// and very few assumptions about the user's page layout.
//
// Make sure all browsers handle this JS library correctly with ES5.
// More information here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
'use strict';
// A wrapper function that does a couple things:
//
// 1. Doesn't pollute the global namespace. This is important for a library.
// 2. Allows us to mount the library in different module systems, as well as
// directly in the browser.
(function() {
// Save the global `this` for use later. In this case, since the library only
// runs in the browser, it will refer to `window`. Also, figure out if we're in IE8
// or not. IE8 will still render correctly, but will be static instead of draggable.
//
// Save a couple long function names that are used frequently.
// This optimization saves around 400 bytes.
var global = this
, isIE8 = global.attachEvent && !global[addEventListener]
, document = global.document
, addEventListener = 'addEventListener'
, removeEventListener = 'removeEventListener'
, getBoundingClientRect = 'getBoundingClientRect'
, isIE8 = global.attachEvent && !global[addEventListener]
, document = global.document
// This library only needs two helper functions:
//
// The first determines which prefixes of CSS calc we need.
// We only need to do this once on startup, when this anonymous function is called.
//
// Tests -webkit, -moz and -o prefixes. Modified from StackOverflow:
// http://stackoverflow.com/questions/16625140/js-feature-detection-to-detect-the-usage-of-webkit-calc-over-calc/16625167#16625167
, calc = (function () {
var el
, prefixes = ["", "-webkit-", "-moz-", "-o-"]
@ -23,6 +47,10 @@ var global = this
}
}
})()
// The second helper function allows elements and string selectors to be used
// interchangeably. In either case an element is returned. This allows us to
// do `Split(elem1, elem2)` as well as `Split('#id1', '#id2')`.
, elementOrSelector = function (el) {
if (typeof el === 'string' || el instanceof String) {
return document.querySelector(el)
@ -31,6 +59,38 @@ var global = this
}
}
// The main function to initialize a split. Split.js thinks about each pair
// of elements as an independant pair. Dragging the gutter between two elements
// only changes the dimensions of elements in that pair. This is key to understanding
// how the following functions operate, since each function is bound to a pair.
//
// A pair object is shaped like this:
//
// {
// a: DOM element,
// b: DOM element,
// aMin: Number,
// bMin: Number,
// dragging: Boolean,
// parent: DOM element,
// isFirst: Boolean,
// isLast: Boolean,
// direction: 'horizontal' | 'vertical'
// }
//
// The basic sequence:
//
// 1. Set defaults to something sane. `options` doesn't have to be passed at all.
// 2. Initialize a bunch of strings based on the direction we're splitting.
// A lot of the behavior in the rest of the library is paramatized down to
// rely on CSS strings and classes.
// 3. Define the dragging helper functions, and a few helpers to go with them.
// 4. Define a few more functions that "balance" the entire split instance.
// Split.js tries it's best to cope with min sizes that don't add up.
// 5. Loop through the elements while pairing them off. Every pair gets an
// `pair` object, a gutter, and special isFirst/isLast properties.
// 6. Actually size the pair elements, insert gutters and attach event listeners.
// 7. Balance all of the pairs to accomodate min sizes as best as possible.
, Split = function (ids, options) {
var dimension
, i
@ -42,8 +102,9 @@ var global = this
, paddingB
, pairs = []
// Set defaults
// 1. Set defaults to something sane. `options` doesn't have to be passed at all,
// so create an options object if none exists. Pixel values 10, 100 and 30 are
// arbitrary but feel natural.
options = typeof options !== 'undefined' ? options : {}
if (typeof options.gutterSize === 'undefined') options.gutterSize = 10
@ -51,6 +112,9 @@ var global = this
if (typeof options.snapOffset === 'undefined') options.snapOffset = 30
if (typeof options.direction === 'undefined') options.direction = 'horizontal'
// 2. Initialize a bunch of strings based on the direction we're splitting.
// A lot of the behavior in the rest of the library is paramatized down to
// rely on CSS strings and classes.
if (options.direction == 'horizontal') {
dimension = 'width'
clientDimension = 'clientWidth'
@ -71,25 +135,43 @@ var global = this
if (!options.cursor) options.cursor = 'ns-resize'
}
// Event listeners for drag events, bound to a pair object.
// Calculate the pair's position and size when dragging starts.
// Prevent selection on start and re-enable it when done.
// 3. Define the dragging helper functions, and a few helpers to go with them.
// Each helper is bound to a pair object that contains it's metadata. This
// also makes it easy to store references to listeners that that will be
// added and removed.
//
// Even though there are no other functions contained in them, aliasing
// this to self saves 50 bytes or so since it's used so frequently.
//
// The pair object saves metadata like dragging state, position and
// event listener references.
//
// startDragging calls `calculateSizes` to store the inital size in the pair object.
// It also adds event listeners for mouse/touch events,
// and prevents selection while dragging so avoid the selecting text.
var startDragging = function (e) {
// Alias frequently used variables to save space. 200 bytes.
var self = this
, a = self.a
, b = self.b
// Call the onDragStart callback.
if (!self.dragging && options.onDragStart) {
options.onDragStart()
}
// Don't actually drag the element. We emulate that in the drag function.
e.preventDefault()
// Set the dragging property of the pair object.
self.dragging = true
// Create two event listeners bound to the same pair object and store
// them in the pair object.
self.move = drag.bind(self)
self.stop = stopDragging.bind(self)
// All the binding. `window` gets the stop events in case we drag out of the elements.
global[addEventListener]('mouseup', self.stop)
global[addEventListener]('touchend', self.stop)
global[addEventListener]('touchcancel', self.stop)
@ -97,10 +179,11 @@ var global = this
self.parent[addEventListener]('mousemove', self.move)
self.parent[addEventListener]('touchmove', self.move)
a[addEventListener]('selectstart', preventSelection)
a[addEventListener]('dragstart', preventSelection)
b[addEventListener]('selectstart', preventSelection)
b[addEventListener]('dragstart', preventSelection)
// Disable selection. Disable!
a[addEventListener]('selectstart', noop)
a[addEventListener]('dragstart', noop)
b[addEventListener]('selectstart', noop)
b[addEventListener]('dragstart', noop)
a.style.userSelect = 'none'
a.style.webkitUserSelect = 'none'
@ -112,11 +195,16 @@ var global = this
b.style.MozUserSelect = 'none'
b.style.pointerEvents = 'none'
// Set the cursor, both on the gutter and the parent element.
// Doing only a, b and gutter causes flickering.
self.gutter.style.cursor = options.cursor
self.parent.style.cursor = options.cursor
// Cache the initial sizes of the pair.
calculateSizes.call(self)
}
// stopDragging is very similar to startDragging in reverse.
, stopDragging = function () {
var self = this
, a = self.a
@ -128,6 +216,7 @@ var global = this
self.dragging = false
// Remove the stored event listeners. This is why we store them.
global[removeEventListener]('mouseup', self.stop)
global[removeEventListener]('touchend', self.stop)
global[removeEventListener]('touchcancel', self.stop)
@ -135,13 +224,15 @@ var global = this
self.parent[removeEventListener]('mousemove', self.move)
self.parent[removeEventListener]('touchmove', self.move)
// Delete them once they are removed. I think this makes a difference
// in memory usage with a lot of splits on one page. But I don't know for sure.
delete self.stop
delete self.move
a[removeEventListener]('selectstart', preventSelection)
a[removeEventListener]('dragstart', preventSelection)
b[removeEventListener]('selectstart', preventSelection)
b[removeEventListener]('dragstart', preventSelection)
a[removeEventListener]('selectstart', noop)
a[removeEventListener]('dragstart', noop)
b[removeEventListener]('selectstart', noop)
b[removeEventListener]('dragstart', noop)
a.style.userSelect = ''
a.style.webkitUserSelect = ''
@ -156,36 +247,70 @@ var global = this
self.gutter.style.cursor = ''
self.parent.style.cursor = ''
}
// drag, where all the magic happens. The logic is really quite simple:
//
// 1. Ignore if the pair is not dragging.
// 2. Get the offset of the event.
// 3. Snap offset to min if within snappable range (within min + snapOffset).
// 4. Actually adjust each element in the pair to offset.
//
// ---------------------------------------------------------------------
// | | <- this.aMin || this.bMin -> | |
// | | | <- this.snapOffset || this.snapOffset -> | | |
// | | | || | | |
// | | | || | | |
// ---------------------------------------------------------------------
// | <- this.start this.size -> |
, drag = function (e) {
var offset
if (!this.dragging) return
// Get the relative position of the event from the first side of the
// pair.
// Get the offset of the event from the first side of the
// pair `this.start`. Supports touch events, but not multitouch, so only the first
// finger `touches[0]` is counted.
if ('touches' in e) {
offset = e.touches[0][clientAxis] - this.start
} else {
offset = e[clientAxis] - this.start
}
// If within snapOffset of min or max, set offset to min or max
if (offset <= this.aMin + options.snapOffset) {
offset = this.aMin
} else if (offset >= this.size - this.bMin - options.snapOffset) {
offset = this.size - this.bMin
// If within snapOffset of min or max, set offset to min or max.
// snapOffset buffers aMin and bMin, so logic is opposite for both.
// Include the appropriate gutter sizes to prevent overflows.
if (offset <= this.aMin + options.snapOffset + this.aGutterSize) {
offset = this.aMin + this.aGutterSize
} else if (offset >= this.size - (this.bMin + options.snapOffset + this.bGutterSize)) {
offset = this.size - (this.bMin + this.bGutterSize)
}
// Actually adjust the size.
adjust.call(this, offset)
// Call the drag callback continously. Don't do anything too intensive
// in this callback.
if (options.onDrag) {
options.onDrag()
}
}
// Cache some important sizes when drag starts, so we don't have to do that
// continously:
//
// `size`: The total size of the pair. First element + second element + first gutter + second gutter.
// `percentage`: The percentage between 0-100 that the pair occupies in the parent.
// `start`: The leading side of the first element.
//
// ------------------------------------------------ - - - - - - - - - - -
// | aGutterSize -> ||| | |
// | ||| | |
// | ||| | |
// | ||| <- bGutterSize | |
// ------------------------------------------------ - - - - - - - - - - -
// | <- start size -> | parentSize -> |
, calculateSizes = function () {
// Calculate the pairs size, and percentage of the parent size
// Figure out the parent size minus padding.
var computedStyle = global.getComputedStyle(this.parent)
, parentSize = this.parent[clientDimension] - parseFloat(computedStyle[paddingA]) - parseFloat(computedStyle[paddingB])
@ -193,13 +318,21 @@ var global = this
this.percentage = Math.min(this.size / parentSize * 100, 100)
this.start = this.a[getBoundingClientRect]()[position]
}
, adjust = function (offset) {
// A size is the same as offset. B size is total size - A size.
// Both sizes are calculated from the initial parent percentage.
// Actually adjust the size of elements `a` and `b` to `offset` while dragging.
// calc is used to allow calc(percentage + gutterpx) on the whole split instance,
// which allows the viewport to be resized without additional logic.
// Element a's size is the same as offset. b's size is total size - a size.
// Both sizes are calculated from the initial parent percentage, then the gutter size is subtracted.
, adjust = function (offset) {
this.a.style[dimension] = calc + '(' + (offset / this.size * this.percentage) + '% - ' + this.aGutterSize + 'px)'
this.b.style[dimension] = calc + '(' + (this.percentage - (offset / this.size * this.percentage)) + '% - ' + this.bGutterSize + 'px)'
}
// 4. Define a few more functions that "balance" the entire split instance.
// Split.js tries it's best to cope with min sizes that don't add up.
// At some point this should go away since it breaks out of the calc(% - px) model.
// Maybe it's a user error if you pass uncomputable minSizes.
, fitMin = function () {
var self = this
, a = self.a
@ -237,9 +370,31 @@ var global = this
fitMinReverse.call(pairs[i])
}
}
, preventSelection = function () { return false }
, setElementSize = function (el, size, gutterSize) {
// Split.js allows setting sizes via numbers (ideally), or if you must,
// by string, like '300px'. This is less than ideal, because it breaks
// the fluid layout that `calc(% - px)` provides. You're on your own if you do that,
// make sure you calculate the gutter size by hand.
if (typeof size !== 'string' && !(size instanceof String)) {
if (!isIE8) {
size = calc + '(' + size + '% - ' + gutterSize + 'px)'
} else {
size = options.sizes[i] + '%'
}
}
el.style[dimension] = size
}
// No-op function to prevent default. Used to prevent selection.
, noop = function () { return false }
// All DOM elements in the split should have a common parent. We can grab
// the first elements parent and hope users read the docs because the
// behavior will be whacky otherwise.
, parent = elementOrSelector(ids[0]).parentNode
// Set default options.sizes to equal percentages of the parent element.
if (!options.sizes) {
var percent = 100 / ids.length
@ -250,6 +405,8 @@ var global = this
}
}
// Standardize minSize to an array if it isn't already. This allows minSize
// to be passed as a number.
if (!Array.isArray(options.minSize)) {
var minSizes = []
@ -260,17 +417,36 @@ var global = this
options.minSize = minSizes
}
// 5. Loop through the elements while pairing them off. Every pair gets a
// `pair` object, a gutter, and isFirst/isLast properties.
//
// Basic logic:
//
// - Starting with the second element `i > 0`, create `pair` objects with
// `a = ids[i - 1]` and `b = ids[i]`
// - Set gutter sizes based on the _pair_ being first/last. The first and last
// pair have gutterSize / 2, since they only have one half gutter, and not two.
// - Create gutter elements and add event listeners.
// - Set the size of the elements, minus the gutter sizes.
//
// -----------------------------------------------------------------------
// | i=0 | i=1 | i=2 | i=3 |
// | | isFirst | | isLast |
// | pair 0 pair 1 pair 2 |
// | | | | |
// -----------------------------------------------------------------------
for (i = 0; i < ids.length; i++) {
var el = elementOrSelector(ids[i])
, isFirst = (i == 1)
, isLast = (i == ids.length - 1)
, size
, isFirstPair = (i == 1)
, isLastPair = (i == ids.length - 1)
, size = options.sizes[i]
, gutterSize = options.gutterSize
, pair
, parentFlexDirection = window.getComputedStyle(parent).flexDirection
, temp
if (i > 0) {
// Create the pair object with it's metadata.
pair = {
a: elementOrSelector(ids[i - 1]),
b: el,
@ -278,21 +454,20 @@ var global = this
bMin: options.minSize[i],
dragging: false,
parent: parent,
isFirst: isFirst,
isLast: isLast,
isFirst: isFirstPair,
isLast: isLastPair,
direction: options.direction
}
// For first and last pairs, first and last gutter width is half.
pair.aGutterSize = options.gutterSize
pair.bGutterSize = options.gutterSize
if (isFirst) {
if (isFirstPair) {
pair.aGutterSize = options.gutterSize / 2
}
if (isLast) {
if (isLastPair) {
pair.bGutterSize = options.gutterSize / 2
}
@ -304,8 +479,13 @@ var global = this
}
}
// Determine the size of the current element. IE8 is supported by
// staticly assigning sizes without draggable gutters. Assigns a string
// to `size`.
//
// IE9 and above
if (!isIE8) {
// Create gutter elements for each pair.
if (i > 0) {
var gutter = document.createElement('div')
@ -320,35 +500,62 @@ var global = this
pair.gutter = gutter
}
// Half-size gutters for first and last elements.
if (i === 0 || i == ids.length - 1) {
gutterSize = options.gutterSize / 2
}
if (typeof options.sizes[i] === 'string' || options.sizes[i] instanceof String) {
size = options.sizes[i]
} else {
size = calc + '(' + options.sizes[i] + '% - ' + gutterSize + 'px)'
}
// IE8 and below
} else {
if (typeof options.sizes[i] === 'string' || options.sizes[i] instanceof String) {
size = options.sizes[i]
} else {
size = options.sizes[i] + '%'
}
}
el.style[dimension] = size
// Set the element size to our determined size.
setElementSize(el, size, gutterSize)
// After the first iteration, and we have a pair object, append it to the
// list of pairs.
if (i > 0) {
pairs.push(pair)
}
}
// Balance the pairs to try to accomodate min sizes.
balancePairs(pairs)
return {
setSizes: function (sizes) {
for (var i = 0; i < sizes.length; i++) {
if (i > 0) {
var pair = pairs[i - 1]
setElementSize(pair.a, sizes[i - 1], pair.aGutterSize)
setElementSize(pair.b, sizes[i], pair.bGutterSize)
}
}
},
collapse: function (i) {
var pair
if (i === pairs.length) {
pair = pairs[i - 1]
calculateSizes.call(pair)
adjust.call(pair, pair.size - Math.max(pair.bGutterSize, pair.aMin))
} else {
pair = pairs[i]
calculateSizes.call(pair)
adjust.call(pair, Math.max(pair.aGutterSize, pair.aMin))
}
},
destroy: function () {
for (var i = 0; i < pairs.length; i++) {
pairs[i].parent.removeChild(pairs[i].gutter)
pairs[i].a.style[dimension] = ''
pairs[i].b.style[dimension] = ''
}
}
}
}
// Play nicely with module systems, and the browser too if you include it raw.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = Split
@ -358,4 +565,5 @@ if (typeof exports !== 'undefined') {
global.Split = Split
}
// Call our wrapper function with the current global. In this case, `window`.
}).call(window);

16
src/library-list.js Normal file
View File

@ -0,0 +1,16 @@
window.jsLibs = [
{ url: 'https://code.jquery.com/jquery-3.1.1.min.js', label: 'jQuery', type: 'js' },
{ url: 'https://ajax.googleapis.com/ajax/libs/angularjs/1.5.9/angular.min.js', label: 'Angular', type: 'js' },
{ url: 'https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.min.js', label: 'React', type: 'js' },
{ url: 'https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.min.js', label: 'React DOM', type: 'js' },
{ url: 'https://unpkg.com/vue@2.1.3/dist/vue.min.js', label: 'Vue.js', type: 'js' },
{ url: 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js', label: 'Three.js', type: 'js' },
{ url: 'https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js', label: 'D3', type: 'js' },
{ url: 'https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js', label: 'Underscore', type: 'js' },
]
window.cssLibs = [
{ url: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css', label: 'Bootstrap', type: 'css' },
{ url: 'https://cdnjs.cloudflare.com/ajax/libs/foundation/6.2.3/foundation.min.css', label: 'Foundation', type: 'css' },
{ url: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css', label: 'Animate.css', type: 'css' },
{ url: 'https://cdnjs.cloudflare.com/ajax/libs/hint.css/2.4.1/hint.min.css', label: 'Hint.css', type: 'css' }
]

20
src/notifications.js Normal file
View File

@ -0,0 +1,20 @@
(function () {
var noticationContainerEL = $('#js-alerts-container');
function addNotification(msg) {
// var n = document.createElement('div');
// div.textContent = msg;
// noticationContainerEL.appendChild(n);
noticationContainerEL.textContent = msg;
noticationContainerEL.classList.add('is-active');
setTimeout(function () {
noticationContainerEL.classList.remove('is-active');
}, 2000)
}
window.alertsService = {
add: addNotification
}
})();

View File

@ -1,13 +1,19 @@
/* global trackEvent */
/* global layoutBtn1, layoutBtn2, layoutBtn3, helpModal, notificationsModal, addLibraryModal,
onboardModal, layoutBtn1, layoutBtn2, layoutBtn3, helpBtn, onboardModal, onboardModal,
addLibraryModal, addLibraryModal, notificationsBtn, notificationsModal, notificationsModal,
notificationsModal, notificationsBtn, codepenBtn, saveHtmlBtn, openBtn, saveBtn, newBtn,
settingsBtn, onboardModal, notificationsBtn */
/* eslint-disable no-extra-semi */
;(function () {
;(function (alertsService) {
/* eslint-enable no-extra-semi */
var editur = window.editur || {};
var version = '1.7.1';
var scope = scope || {};
var version = '2.0.0';
window.$ = document.querySelector.bind(document);
window.$all = document.querySelectorAll.bind(document);
if (window.DEBUG) {
window.scope = scope;
}
var HtmlModes = {
HTML: 'html',
@ -43,95 +49,322 @@
, jsMode = JsModes.JS
, cssMode = CssModes.CSS
, sass
, currentItem
, savedItems
, minCodeWrapSize = 33
, mainSplitInstance
, codeSplitInstance
// TODO: for legacy reasons when. Will be refactored as global preferences.
, prefs = {}
// DOM nodes
, frame = $('#demo-frame')
, htmlCode = $('#js-html-code')
, cssCode = $('#js-css-code')
, jsCode = $('#js-js-code')
, layoutBtn1 = $('#js-layout-btn-1')
, layoutBtn2 = $('#js-layout-btn-2')
, layoutBtn3 = $('#js-layout-btn-3')
, helpBtn = $('#js-help-btn')
, helpModal = $('#js-help-modal')
, codepenBtn = $('#js-codepen-btn')
, codepenForm = $('#js-codepen-form')
, saveHtmlBtn = $('#js-save-html')
, settingsBtn = $('#js-settings-btn')
, notificationsBtn = $('#js-notifications-btn')
, notificationsModal = $('#js-notifications-modal')
, savedItemsPane = $('#js-saved-items-pane')
, savedItemsPaneCloseBtn = $('#js-saved-items-pane-close-btn')
, htmlModelLabel = $('#js-html-mode-label')
, cssModelLabel = $('#js-css-mode-label')
, jsModelLabel = $('#js-js-mode-label')
, titleInput = $('#js-title-input')
, addLibrarySelect = $('#js-add-library-select')
, addLibraryBtn = $('#js-add-library-btn')
, externalJsTextarea = $('#js-external-js')
, externalCssTextarea = $('#js-external-css')
;
editur.cm = {};
editur.demoFrameDocument = frame.contentDocument || frame.contentWindow.document;
scope.cm = {};
scope.demoFrameDocument = frame.contentDocument || frame.contentWindow.document;
// https://github.com/substack/semver-compare/blob/master/index.js
function semverCompare (a, b) {
var pa = a.split('.');
var pb = b.split('.');
for (var i = 0; i < 3; i++) {
var na = Number(pa[i]);
var nb = Number(pb[i]);
if (na > nb) { return 1; }
if (nb > na) { return -1; }
if (!isNaN(na) && isNaN(nb)) { return 1; }
if (isNaN(na) && !isNaN(nb)) { return -1; }
// Check all the code wrap if they are minimized or not
function updateCodeWrapCollapseStates() {
clearTimeout(updateCodeWrapCollapseStates.timeout);
updateCodeWrapCollapseStates.timeout = setTimeout(function () {
[ htmlCode, cssCode, jsCode ].forEach(function (el) {
var bounds = el.getBoundingClientRect();
if (bounds[currentLayoutMode === 2 ? 'width' : 'height'] < 100) {
el.classList.add('is-minimized');
} else {
el.classList.remove('is-minimized');
}
});
}, 50);
}
function toggleCodeWrapCollapse(codeWrapEl) {
if (codeWrapEl.classList.contains('is-minimized')) {
codeWrapEl.classList.remove('is-minimized');
codeSplitInstance.setSizes([ 33.3, 33.3, 33.3 ]);
} else {
codeSplitInstance.collapse(parseInt(codeWrapEl.dataset.codeWrapId, 10));
codeWrapEl.classList.add('is-minimized');
}
return 0;
}
function resetSplitting() {
var gutters = $all('.gutter');
for (var i = gutters.length; i--;) {
gutters[i].remove();
if (codeSplitInstance) {
codeSplitInstance.destroy();
}
if (mainSplitInstance) {
mainSplitInstance.destroy();
}
$('#js-html-code').setAttribute('style', '');
$('#js-css-code').setAttribute('style', '');
$('#js-js-code').setAttribute('style', '');
$('#js-code-side').setAttribute('style', '');
$('#js-demo-side').setAttribute('style', '');
Split(['#js-html-code', '#js-css-code', '#js-js-code'], {
var options = {
direction: (currentLayoutMode === 2 ? 'horizontal' : 'vertical'),
minSize: 34,
gutterSize: 6
});
Split(['#js-code-side', '#js-demo-side' ], {
minSize: minCodeWrapSize,
gutterSize: 6,
onDragStart: function () {
document.body.classList.add('is-dragging');
},
onDragEnd: function () {
updateCodeWrapCollapseStates();
document.body.classList.remove('is-dragging');
}
};
if (currentItem && currentItem.sizes) {
options.sizes = currentItem.sizes;
} else {
options.sizes = [ 33.33, 33.33, 33.33 ];
}
// utils.log('reset spliiting', options.sizes)
codeSplitInstance = Split(['#js-html-code', '#js-css-code', '#js-js-code'], options);
mainSplitInstance = Split(['#js-code-side', '#js-demo-side' ], {
direction: (currentLayoutMode === 2 ? 'vertical' : 'horizontal'),
minSize: 34,
gutterSize: 6
});
}
function toggleLayout(mode) {
if (currentLayoutMode === mode) {
utils.log('setsize', currentItem.sizes || [ 33.33, 33.33, 33.33 ]);
codeSplitInstance.setSizes(currentItem.sizes || [ 33.33, 33.33, 33.33 ]);
currentLayoutMode = mode;
return;
}
currentLayoutMode = mode;
$('#js-layout-btn-1').classList.remove('selected');
$('#js-layout-btn-2').classList.remove('selected');
$('#js-layout-btn-3').classList.remove('selected');
$('#js-layout-btn-' + mode).classList.add('selected');
layoutBtn1.classList.remove('selected');
layoutBtn2.classList.remove('selected');
layoutBtn3.classList.remove('selected');
$('#layoutBtn' + mode).classList.add('selected');
document.body.classList.remove('layout-1');
document.body.classList.remove('layout-2');
document.body.classList.remove('layout-3');
document.body.classList.add('layout-' + mode);
resetSplitting();
trackEvent('ui', 'toggleLayout', mode);
}
function saveSetting(setting, value) {
function onExternalLibChange() {
utils.log('onExternalLibChange');
updateExternalLibUi();
scope.setPreviewContent();
}
function updateExternalLibUi() {
// Calculate no. of external libs
var noOfExternalLibs = 0;
noOfExternalLibs += externalJsTextarea.value.split('\n').filter((lib) => !!lib).length;
noOfExternalLibs += externalCssTextarea.value.split('\n').filter((lib) => !!lib).length;
if (noOfExternalLibs) {
$('#js-external-lib-count').textContent = noOfExternalLibs;
$('#js-external-lib-count').style.display = 'inline';
} else {
$('#js-external-lib-count').style.display = 'none';
}
}
function saveSetting(setting, value, cb) {
var obj = {};
obj[setting] = value;
chrome.storage.local.set(obj, function() {
chrome.storage.local.set(obj, cb || function(){});
}
// Save current item to storage
function saveItem() {
var isNewItem = !currentItem.id;
currentItem.id = currentItem.id || ('item-' + utils.generateRandomId());
saveCode();
// Push into the items hash if its a new item being saved
if (isNewItem) {
chrome.storage.local.get({
items: {}
}, function (result) {
result.items[currentItem.id] = true;
chrome.storage.local.set({
items: result.items
});
});
}
}
function saveCode(key) {
currentItem.title = titleInput.value;
currentItem.html = scope.cm.html.getValue();
currentItem.css = scope.cm.css.getValue();
currentItem.js = scope.cm.js.getValue();
currentItem.htmlMode = htmlMode;
currentItem.cssMode = cssMode;
currentItem.jsMode = jsMode;
currentItem.updatedOn = Date.now();
currentItem.layoutMode = currentLayoutMode;
currentItem.externalLibs = { js: externalJsTextarea.value, css: externalCssTextarea.value };
// debugger;
var dimensionProperty = currentLayoutMode === 2 ? 'width' : 'height';
var sizes;
try {
sizes = [
+htmlCode.style[dimensionProperty].match(/([\d.]+)%/)[1],
+cssCode.style[dimensionProperty].match(/([\d.]+)%/)[1],
+jsCode.style[dimensionProperty].match(/([\d.]+)%/)[1]
];
} catch (e) {
sizes = [ 33.33, 33.33, 33.33 ]
} finally {
currentItem.sizes = sizes;
utils.log('saving key', key || currentItem.id, currentItem)
saveSetting(key || currentItem.id, currentItem, function () {
alertsService.add('Item saved.');
});
}
}
function populateItemsInSavedPane(items) {
var html = '';
if (items.length) {
// TODO: sort desc. by updation date
items.sort(function (a, b) {
return b.updatedOn - a.updatedOn;
});
items.forEach(function (item) {
html += '<div class="js-saved-item-tile saved-item-tile" data-item-id="' + item.id + '">'
+ '<a class="js-saved-item-tile__close-btn saved-item-tile__close-btn hint--left" aria-label="Remove">X</a>'
+ '<h3 class="saved-item-tile__title">' + item.title + '</h3><span class="saved-item-tile__meta">Last updated: ' + utils.getHumanDate(item.updatedOn) + '</span></div>';
});
} else {
html += '<h2 class="opacity--30">Nothing saved here.</h2>'
}
savedItemsPane.querySelector('#js-saved-items-wrap').innerHTML = html;
toggleSavedItemsPane();
// HACK: Set overflow after sometime so that the items can animate without getting cropped.
// setTimeout(() => $('#js-saved-items-wrap').style.overflowY = 'auto', 1000);
}
function toggleSavedItemsPane(shouldOpen) {
if (shouldOpen === false) {
savedItemsPane.classList.remove('is-open');
} else {
savedItemsPane.classList.toggle('is-open');
}
document.body.classList[savedItemsPane.classList.contains('is-open') ? 'add' : 'remove']('overlay-visible');
}
function openSavedItemsPane() {
chrome.storage.local.get('items', function (result) {
var itemIds = Object.getOwnPropertyNames(result.items || {}),
items = [];
if (!itemIds.length) {
populateItemsInSavedPane([]);
return;
}
savedItems = savedItems || [];
trackEvent('fn', 'fetchItems', '', itemIds.length);
for (let i = 0; i < itemIds.length; i++) {
/* eslint-disable no-loop-func */
chrome.storage.local.get(itemIds[i], function (itemResult) {
savedItems[itemIds[i]] = itemResult[itemIds[i]];
items.push(itemResult[itemIds[i]]);
// Check if we have all items now.
if (itemIds.length === items.length) {
populateItemsInSavedPane(items);
}
});
/* eslint-enable no-loop-func */
}
});
}
function saveCode() {
var code = {
html: editur.cm.html.getValue(),
css: editur.cm.css.getValue(),
js: editur.cm.js.getValue()
function createNewItem() {
var d = new Date();
currentItem = {
title: 'Untitled ' + d.getDate() + '-' + d.getMonth() + '-' + d.getHours() + ':' + d.getMinutes(),
html: '',
css: '',
js: '',
externalLibs: { js: '', css: '' },
layoutMode: currentLayoutMode
};
saveSetting('code', code);
alertsService.add('New item created');
refreshEditor();
}
function openItem(itemId) {
currentItem = savedItems[itemId];
// codeSplitInstance.setSizes([ 33.3, 33.3, 33.3 ]);
refreshEditor();
alertsService.add('Saved item loaded');
}
function removeItem(itemId) {
var itemTile = document.querySelector('.js-saved-item-tile[data-item-id="' + itemId + '"]');
var answer = confirm(`Are you sure you want to delete "${savedItems[itemId].title}"?`);
if (!answer) { return; }
itemTile.remove();
// Remove from items list
chrome.storage.local.get({
items: {}
}, function (result) {
delete result.items[itemId]
chrome.storage.local.set({
items: result.items
});
});
// Remove individual item too.
chrome.storage.local.remove(itemId, function () {
alertsService.add('Item removed.');
// This item is open in the editor. Lets open a new one.
if (currentItem.id === itemId) {
createNewItem();
}
});
trackEvent('fn', 'itemRemoved');
}
function refreshEditor() {
titleInput.value = currentItem.title || 'Untitled';
externalJsTextarea.value = (currentItem.externalLibs && currentItem.externalLibs.js) || '';
externalCssTextarea.value = (currentItem.externalLibs && currentItem.externalLibs.css) || '';
externalJsTextarea.dispatchEvent(new Event('change'));
scope.cm.html.setValue(currentItem.html);
scope.cm.css.setValue(currentItem.css);
scope.cm.js.setValue(currentItem.js);
scope.cm.html.refresh();
scope.cm.css.refresh();
scope.cm.js.refresh();
updateHtmlMode(currentItem.htmlMode || prefs.htmlMode || HtmlModes.HTML);
updateJsMode(currentItem.jsMode || prefs.jsMode || JsModes.JS);
updateCssMode(currentItem.cssMode || prefs.cssMode || CssModes.CSS);
toggleLayout(currentItem.layoutMode || prefs.layoutMode);
}
function closeAllOverlays() {
helpModal.classList.remove('is-modal-visible');
notificationsModal.classList.remove('is-modal-visible');
addLibraryModal.classList.remove('is-modal-visible');
onboardModal.classList.remove('is-modal-visible');
toggleSavedItemsPane(false);
}
/**
@ -167,41 +400,38 @@
htmlMode = value;
htmlModelLabel.textContent = modes[value].label;
handleModeRequirements(value);
editur.cm.html.setOption('mode', modes[value].cmMode);
CodeMirror.autoLoadMode(editur.cm.html, modes[value].cmMode);
chrome.storage.sync.set({
htmlMode: value
}, function () {});
scope.cm.html.setOption('mode', modes[value].cmMode);
CodeMirror.autoLoadMode(scope.cm.html, modes[value].cmMode);
trackEvent('ui', 'updateCodeMode', 'html', value);
}
function updateCssMode(value) {
cssMode = value;
cssModelLabel.textContent = modes[value].label;
handleModeRequirements(value);
editur.cm.css.setOption('mode', modes[value].cmMode);
CodeMirror.autoLoadMode(editur.cm.css, modes[value].cmMode);
chrome.storage.sync.set({
cssMode: value
}, function () {});
scope.cm.css.setOption('mode', modes[value].cmMode);
CodeMirror.autoLoadMode(scope.cm.css, modes[value].cmMode);
trackEvent('ui', 'updateCodeMode', 'css', value);
}
function updateJsMode(value) {
jsMode = value;
jsModelLabel.textContent = modes[value].label;
handleModeRequirements(value);
editur.cm.js.setOption('mode', modes[value].cmMode);
CodeMirror.autoLoadMode(editur.cm.js, modes[value].cmMode);
scope.cm.js.setOption('mode', modes[value].cmMode);
CodeMirror.autoLoadMode(scope.cm.js, modes[value].cmMode);
trackEvent('ui', 'updateCodeMode', 'js', value);
// FIXME: Will be saved as part of scope settings
/*
chrome.storage.sync.set({
jsMode: value
}, function () {});
trackEvent('ui', 'updateCodeMode', 'js', value);
*/
}
// computeHtml, computeCss & computeJs evaluate the final code according
// to whatever mode is selected and resolve the returned promise with the code.
function computeHtml() {
var d = deferred();
var code = editur.cm.html.getValue();
var code = scope.cm.html.getValue();
if (htmlMode === HtmlModes.HTML) {
d.resolve(code);
} else if (htmlMode === HtmlModes.MARKDOWN) {
@ -214,79 +444,162 @@
}
function computeCss() {
var d = deferred();
var code = editur.cm.css.getValue();
var code = scope.cm.css.getValue();
cleanupErrors('css');
if (cssMode === CssModes.CSS) {
d.resolve(code);
} else if (cssMode === CssModes.SCSS) {
sass.compile(code, function(result) {
// Something as wrong
if (result.line && result.message) {
showErrors('css', [ { lineNumber: result.line - 1, message: result.message } ]);
}
d.resolve(result.text);
});
} else if (cssMode === CssModes.LESS) {
less.render(code).then(function (result) {
d.resolve(result.css);
}, function (error) {
showErrors('css', [ { lineNumber: error.line, message: error.message } ]);
});
}
return d.promise;
}
function computeJs() {
function computeJs(shouldPreventInfiniteLoops) {
var d = deferred();
var code = editur.cm.js.getValue();
var code = scope.cm.js.getValue();
cleanupErrors('js');
var ast;
if (jsMode === JsModes.JS) {
d.resolve(code);
try {
ast = esprima.parse(code, {
tolerant: true
});
} catch (e) {
showErrors('js', [ { lineNumber: e.lineNumber - 1, message: e.description } ]);
} finally {
if (shouldPreventInfiniteLoops !== false) {
utils.addInfiniteLoopProtection(ast);
}
d.resolve(escodegen.generate(ast));
}
} else if (jsMode === JsModes.COFFEESCRIPT) {
d.resolve(CoffeeScript.compile(code, { bare: true }));
var coffeeCode;
try {
coffeeCode = CoffeeScript.compile(code, { bare: true });
} catch (e) {
showErrors('js', [ { lineNumber: e.location.first_line, message: e.message } ]);
} finally {
ast = esprima.parse(coffeeCode, {
tolerant: true
});
if (shouldPreventInfiniteLoops !== false) {
utils.addInfiniteLoopProtection(ast);
}
d.resolve(escodegen.generate(ast));
}
} else if (jsMode === JsModes.ES6) {
d.resolve(Babel.transform(editur.cm.js.getValue(), { presets: ['es2015'] }).code);
try {
ast = esprima.parse(code, {
tolerant: true
});
} catch (e) {
showErrors('js', [ { lineNumber: e.lineNumber - 1, message: e.description } ]);
} finally {
if (shouldPreventInfiniteLoops !== false) {
utils.addInfiniteLoopProtection(ast);
}
d.resolve(Babel.transform(escodegen.generate(ast), { presets: ['es2015'] }).code);
}
}
return d.promise;
}
window.previewException = function (error) {
console.error('Possible infinite loop detected.', error.stack)
}
window.onunload = function () {
saveCode();
saveCode('code');
};
function createPreviewFile(html, css, js) {
var contents = '<html>\n<head>\n<style>\n' + css + '\n</style>\n</head>\n<body>\n' + html + '\n<script>\n' + js + '\n</script></body>\n</html>';
function cleanupErrors(lang) {
scope.cm[lang].clearGutter('error-gutter');
}
function showErrors(lang, errors) {
var editor = scope.cm[lang];
errors.forEach(function (e) {
editor.operation(function () {
var n = document.createElement('div');
n.setAttribute('data-title', e.message);
n.classList.add('gutter-error-marker');
editor.setGutterMarker(e.lineNumber, 'error-gutter', n);
});
});
}
// Track if people are actually writing code.
function getCompleteHtml(html, css, js) {
var externalJs = externalJsTextarea.value.split('\n').reduce(function (scripts, url) {
return scripts + (url ? '\n<script src="' + url + '"></script>' : '');
}, '');
var externalCss = externalCssTextarea.value.split('\n').reduce(function (links, url) {
return links + (url ? '\n<link rel="stylesheet" href="' + url + '"></link>' : '');
}, '');
var contents = '<html>\n<head>\n'
+ externalCss + '\n'
+ '<style>\n' + css + '\n</style>\n'
+ '</head>\n'
+ '<body>\n' + html + '\n'
+ externalJs + '\n<script>\n' + js + '\n//# sourceURL=userscript.js</script></body>\n</html>';
return contents;
}
function createPreviewFile(html, css, js) {
var contents = getCompleteHtml(html, css, js);
var fileWritten = false;
var blob = new Blob([ contents ], { type: "text/plain;charset=UTF-8" });
// Track if people have written code.
if (!trackEvent.hasTrackedCode && (html || css || js)) {
trackEvent('fn', 'hasCode');
trackEvent.hasTrackedCode = true;
}
// Track when people actually are working
// Track when people actually are working.
trackEvent.previewCount = (trackEvent.previewCount || 0) + 1;
if (trackEvent.previewCount === 4) {
trackEvent('fn', 'usingPreview');
}
var fileWritten = false;
var blob = new Blob([ contents ], { type: "text/plain;charset=UTF-8" });
function errorHandler() { console.log(arguments); }
function errorHandler() { utils.log(arguments); }
window.webkitRequestFileSystem(window.TEMPORARY, 1024 * 1024 * 5, function(fs){
fs.root.getFile('preview.html', { create: true }, function(fileEntry) {
fileEntry.createWriter(function(fileWriter) {
function onWriteComplete() {
if (fileWritten) {
frame.src = 'filesystem:chrome-extension://' + chrome.i18n.getMessage('@@extension_id') + '/temporary/' + 'preview.html';
frame.src = 'filesystem:chrome-extension://'
+ chrome.i18n.getMessage('@@extension_id') + '/temporary/' + 'preview.html';
}
else {
fileWritten = true;
// Set the write pointer to starting of file
fileWriter.seek(0);
fileWriter.write(blob);
}
}
fileWriter.onwriteend = onWriteComplete;
// Empty the file contents
fileWriter.truncate(0)
}, errorHandler);
}, errorHandler);
}, errorHandler);
}
editur.setPreviewContent = function () {
scope.setPreviewContent = function () {
var htmlPromise = computeHtml();
var cssPromise = computeCss();
var jsPromise = computeJs();
@ -298,15 +611,13 @@
function saveFile() {
var htmlPromise = computeHtml();
var cssPromise = computeCss();
var jsPromise = computeJs();
var jsPromise = computeJs(false);
Promise.all([htmlPromise, cssPromise, jsPromise]).then(function (result) {
var html = result[0],
css = result[1],
js = result[2];
var fileContent = '<html><head>\n<style>\n'
+ css + '\n</style>\n</head>\n<body>\n'
+ html + '\n<script>\n' + js + '\n</script>\n\n</body>\n</html>';
var fileContent = getCompleteHtml(html, css, js);
var d = new Date();
var fileName = [ 'web-maker', d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds() ].join('-');
@ -335,55 +646,88 @@
tabMode: 'indent',
keyMap: 'sublime',
theme: 'monokai',
lint: !!options.lint,
gutters: options.gutters || [],
// cursorScrollMargin: '20', has issue with scrolling
profile: options.profile || ''
});
cm.on('change', function onChange() {
clearTimeout(updateTimer);
updateTimer = setTimeout(function () {
editur.setPreviewContent();
scope.setPreviewContent();
}, updateDelay);
});
return cm;
}
editur.cm.html = initEditor(htmlCode, {
scope.cm.html = initEditor(htmlCode, {
mode: 'htmlmixed',
profile: 'xhtml'
});
emmetCodeMirror(editur.cm.html);
editur.cm.css = initEditor(cssCode, {
mode: 'css'
emmetCodeMirror(scope.cm.html);
scope.cm.css = initEditor(cssCode, {
mode: 'css',
gutters: [ 'error-gutter' ]
});
editur.cm.js = initEditor(jsCode, {
mode: 'javascript'
Inlet(scope.cm.css);
scope.cm.js = initEditor(jsCode, {
mode: 'javascript',
gutters: [ 'error-gutter' ]
});
Inlet(scope.cm.js);
function openSettings() {
if (chrome.runtime.openOptionsPage) {
// New way to open options pages, if supported (Chrome 42+).
// Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=601997
// Until this bug fixes, use the
// fallback.
chrome.runtime.openOptionsPage();
} else {
// Fallback.
chrome.tabs.create({
url: 'chrome://extensions?options=' + chrome.i18n.getMessage('@@extension_id')
});
}
}
scope.onModalSettingsLinkClick = function () {
openSettings();
trackEvent('ui', 'onboardSettingsBtnClick');
}
function compileNodes() {
var nodes = [].slice.call($all('[d-click]'));
nodes.forEach(function (el) {
el.addEventListener('click', function (e) {
scope[el.getAttribute('d-click')].call(window, e)
});
})
}
function init () {
var lastCode;
CodeMirror.modeURL = "lib/codemirror/mode/%N/%N.js";
function getToggleLayoutButtonListener(mode) {
return function () {
saveSetting('layoutMode', mode);
trackEvent('ui', 'toggleLayoutClick', mode);
toggleLayout(mode);
return false;
};
}
layoutBtn1.addEventListener('click', getToggleLayoutButtonListener(1));
layoutBtn2.addEventListener('click', getToggleLayoutButtonListener(2));
layoutBtn3.addEventListener('click', getToggleLayoutButtonListener(3));
layoutBtn1.addEventListener('click', function () { saveSetting('layoutMode', 1); toggleLayout(1); return false; });
layoutBtn2.addEventListener('click', function () { saveSetting('layoutMode', 2); toggleLayout(2); return false; });
layoutBtn3.addEventListener('click', function () { saveSetting('layoutMode', 3); toggleLayout(3); return false; });
helpBtn.addEventListener('click', function () {
utils.onButtonClick(helpBtn, function () {
helpModal.classList.toggle('is-modal-visible');
document.body.classList[onboardModal.classList.contains('is-modal-visible') ? 'add' : 'remove']('overlay-visible');
trackEvent('ui', 'helpButtonClick');
return false;
});
utils.onButtonClick(addLibraryBtn, function () {
addLibraryModal.classList.toggle('is-modal-visible');
document.body.classList[addLibraryModal.classList.contains('is-modal-visible') ? 'add' : 'remove']('overlay-visible');
trackEvent('ui', 'addLibraryButtonClick');
});
notificationsBtn.addEventListener('click', function () {
notificationsModal.classList.toggle('is-modal-visible');
document.body.classList[notificationsModal.classList.contains('is-modal-visible') ? 'add' : 'remove']('overlay-visible');
if (notificationsModal.classList.contains('is-modal-visible') && !hasSeenNotifications) {
hasSeenNotifications = true;
notificationsBtn.classList.remove('has-new');
@ -398,9 +742,9 @@
codepenBtn.addEventListener('click', function (e) {
var json = {
title: 'A Web Maker experiment',
html: editur.cm.html.getValue(),
css: editur.cm.css.getValue(),
js: editur.cm.js.getValue(),
html: scope.cm.html.getValue(),
css: scope.cm.css.getValue(),
js: scope.cm.js.getValue(),
/* eslint-disable camelcase */
html_pre_processor: modes[htmlMode].codepenVal,
@ -418,9 +762,41 @@
e.preventDefault();
});
saveHtmlBtn.addEventListener('click', function () {
trackEvent('ui', 'saveHtmlClick');
utils.onButtonClick(saveHtmlBtn, function () {
saveFile();
trackEvent('ui', 'saveHtmlClick');
});
utils.onButtonClick(openBtn, function () {
openSavedItemsPane();
trackEvent('ui', 'openBtnClick');
});
utils.onButtonClick(saveBtn, function () {
trackEvent('ui', 'saveBtnClick', currentItem.id ? 'saved' : 'new');
saveItem();
});
utils.onButtonClick(newBtn, function () {
createNewItem();
trackEvent('ui', 'newBtnClick');
});
utils.onButtonClick(savedItemsPaneCloseBtn, toggleSavedItemsPane);
utils.onButtonClick(savedItemsPane, function (e) {
if (e.target.classList.contains('js-saved-item-tile')) {
setTimeout(function () {
openItem(e.target.dataset.itemId);
}, 350);
toggleSavedItemsPane();
}
if (e.target.classList.contains('js-saved-item-tile__close-btn')) {
utils.log('removing', e.target.parentElement)
removeItem(e.target.parentElement.dataset.itemId);
}
});
titleInput.addEventListener('blur', function () {
if (currentItem.id) {
saveItem();
trackEvent('ui', 'titleChanged');
}
});
// Attach listeners on mode change menu items
@ -442,44 +818,86 @@
});
});
// Collapse btn event listeners
var collapseBtns = [].slice.call($all('.js-code-collapse-btn'));
collapseBtns.forEach(function (btn) {
btn.addEventListener('click', function (e) {
var codeWrapParent = e.currentTarget.parentElement.parentElement.parentElement;
toggleCodeWrapCollapse(codeWrapParent);
trackEvent('ui', 'paneCollapseBtnClick', codeWrapParent.dataset.type);
return false;
});
});
// Update code wrap collapse states whenever any of them transitions due to any
// reason.
[ htmlCode, cssCode, jsCode ].forEach(function (el) {
el.addEventListener('transitionend', function() {
updateCodeWrapCollapseStates();
});
});
window.addEventListener('keydown', function (event) {
if ((event.ctrlKey || event.metaKey) && (event.keyCode === 83)){
// Ctrl/⌘ + S
if ((event.ctrlKey || event.metaKey) && (event.keyCode === 83)) {
event.preventDefault();
trackEvent('ui', 'saveFileKeyboardShortcut');
saveFile();
saveItem();
trackEvent('ui', 'saveItemKeyboardShortcut');
}
// Ctrl/⌘ + O
else if ((event.ctrlKey || event.metaKey) && (event.keyCode === 79)) {
event.preventDefault();
openSavedItemsPane();
trackEvent('ui', 'openCreationKeyboardShortcut');
}
else if (event.keyCode === 27) {
closeAllOverlays();
}
});
window.addEventListener('click', function(e) {
if (typeof e.target.className === 'string' && e.target.className.indexOf('modal-overlay') !== -1) {
helpModal.classList.remove('is-modal-visible');
notificationsModal.classList.remove('is-modal-visible');
closeAllOverlays();
}
});
window.addEventListener('dblclick', function(e) {
var target = e.target;
if (target.classList.contains('js-code-wrap__header')) {
var codeWrapParent = target.parentElement;
toggleCodeWrapCollapse(codeWrapParent);
trackEvent('ui', 'paneHeaderDblClick', codeWrapParent.dataset.type);
}
});
settingsBtn.addEventListener('click', function() {
if (!chrome.runtime.openOptionsPage) {
// New way to open options pages, if supported (Chrome 42+).
// Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=601997
// Until this bug fixes, use the
// fallback.
chrome.runtime.openOptionsPage();
} else {
// Fallback.
chrome.tabs.create({
url: 'chrome://extensions?options=' + chrome.i18n.getMessage('@@extension_id')
});
}
utils.onButtonClick(settingsBtn, function() {
openSettings();
trackEvent('ui', 'settingsBtnClick');
return false;
});
// Initialize add library select box
var libOptions = window.jsLibs.reduce(
(html, lib) => html + `<option data-type="${lib.type}" value="${lib.url}">${lib.label}</option>`,
'');
addLibrarySelect.children[1].innerHTML = libOptions;
libOptions = window.cssLibs.reduce(
(html, lib) => html + `<option data-type="${lib.type}" value="${lib.url}">${lib.label}</option>`,
'');
addLibrarySelect.children[2].innerHTML = libOptions;
addLibrarySelect.addEventListener('change', function onSelectChange(e) {
var target = e.target;
$('#js-external-' + target.selectedOptions[0].dataset.type).value += '\n' + target.value;
trackEvent('ui', 'addLibrarySelect', target.selectedOptions[0].label);
onExternalLibChange();
});
externalJsTextarea.addEventListener('change', onExternalLibChange);
externalCssTextarea.addEventListener('change', onExternalLibChange);
chrome.storage.local.get({
layoutMode: 1,
code: ''
}, function localGetCallback(result) {
toggleLayout(result.layoutMode);
prefs.layoutMode = result.layoutMode;
if (result.code) {
lastCode = result.code;
}
@ -493,30 +911,47 @@
cssMode: 'css'
}, function syncGetCallback(result) {
if (result.preserveLastCode && lastCode) {
editur.cm.html.setValue(lastCode.html);
editur.cm.css.setValue(lastCode.css);
editur.cm.js.setValue(lastCode.js);
editur.cm.html.refresh();
editur.cm.css.refresh();
editur.cm.js.refresh();
if (lastCode.id) {
chrome.storage.local.get(lastCode.id, function (itemResult) {
utils.log('Load item ', lastCode.id)
currentItem = itemResult[lastCode.id];
refreshEditor();
});
} else {
utils.log('Load last unsaved item');
currentItem = lastCode;
refreshEditor();
}
} else {
createNewItem();
}
updateHtmlMode(result.htmlMode);
updateJsMode(result.jsMode);
updateCssMode(result.cssMode);
prefs.htmlMode = result.htmlmode;
prefs.cssMode = result.cssMode;
prefs.jsMode = result.jsMode;
});
// Check for new version notifications
chrome.storage.sync.get({
lastSeenVersion: ''
}, function syncGetCallback(result) {
// console.log(result, hasSeenNotifications, version);
if (!result.lastSeenVersion || semverCompare(result.lastSeenVersion, version) === -1) {
// Check if new user
if (!result.lastSeenVersion) {
onboardModal.classList.add('is-modal-visible');
trackEvent('ui', 'onboardModalSeen');
}
// console.utils.log(result, hasSeenNotifications, version);
if (!result.lastSeenVersion || utils.semverCompare(result.lastSeenVersion, version) === -1) {
notificationsBtn.classList.add('has-new');
hasSeenNotifications = false;
}
});
requestAnimationFrame(compileNodes);
}
// Set few stuff on a 'scope' object so that they can be referenced dynamically.
scope.closeAllOverlays = closeAllOverlays;
init();
})();
})(window.alertsService);

616
src/style.css Normal file
View File

@ -0,0 +1,616 @@
@font-face {
font-family: 'Inconsolata';
font-style: normal;
font-weight: 400;
src: local('Inconsolata'), url(../Inconsolata-Regular.ttf) format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
:root {
--color-bg: #252637;
--color-sidebar: #3A2B63;
}
body {
margin: 0;
padding: 0;
background: rgba(0, 0, 0, 0.5);
background: var(--color-bg);
color: rgba(255,255,255,0.9);
min-height: 100vh;
font-family: Helvetica, arial;
}
h1 {
margin-top: 0;
}
a { text-decoration: none; color: crimson; cursor: pointer; }
/*a:hover { text-decoration: underline; }*/
.flex { display: flex; }
.flex-grow { flex-grow: 1; }
.flex-v-center { align-items: center; }
.fr { float: right; }
.relative { position: relative; }
.tac { text-align: center; }
.full-width { width: 100%; }
.opacity--30 { opacity: 0.3; }
[class*="hint--"]:after {
text-transform: none;
}
.caret {
display: inline-block;
width: 0;
height: 0;
border: 6px solid transparent;
border-top-color: currentColor;
position: relative;
top: 5px;
margin-left: 8px;
}
a > svg {
fill: rgba(255, 255, 255, 0.2)
}
select, input[type="text"], textarea {
padding: 3px 5px;
font-size: inherit;
}
.btn {
display: inline-block;
border: 0;
background: #0074d9;
color: white;
font-size: inherit;
border-radius: 3px;
padding: 7px 15px;
cursor: pointer;
transition: 0.2s ease;
}
.btn:hover {
text-decoration: none;
box-shadow: 0 3px 5px 0 rgba(0,0,0,0.15);
}
.star:after {
content: '★';
color: #eee333;
}
.main-container {
position: absolute;
left: 0; right: 0;
top: 0; bottom: 0;
display: flex;
flex-direction: column;
will-change: -webkit-filter;
transition: 0.10s ease 0.2s;
}
.overlay-visible .main-container {
transition-duration: 0.5s;
transition-delay: 0.4s;
-webkit-filter: blur(3px);
}
.code-side,
.demo-side {
flex-basis: inherit;
position: relative;
}
.layout-3 .content-wrap {
flex-direction: row-reverse;
}
.code-side {
display: flex;
flex-direction: column;
width: 50%;
}
.layout-2 .content-wrap {
flex-direction: column;
}
.layout-2 .code-side {
flex-direction: row;
width: auto;
}
.code-wrap {
display: flex;
flex-direction: column;
flex-basis: inherit;
height: 33%;
overflow: hidden;
position: relative;
background: var(--color-bg);
transition: height 0.30s ease, width 0.30s ease;
will-change: height;
}
.layout-2 .code-wrap.is-minimized {
flex-direction: row;
}
.is-dragging .code-wrap {
transition: none;
}
.layout-2 .code-wrap {
height: auto;
width: 33%;
}
.code-wrap:nth-of-type(3) {
animation-delay: 0.3s;
}
.code-wrap:nth-of-type(5) {
animation-delay: 0.4s;
}
.code-wrap__header {
display: flex;
flex-shrink: 0;
justify-content: space-between;
align-items: center;
padding: 5px 10px;
background: rgba(0,0,0,0.20);
color: #888;
border-bottom: 1px solid rgba(0,0,0,0.3);
font-weight: bold;
}
.code-wrap__header-label {
/*transform: translate(0px) scale(1.2);*/
display: inline-block;
font-size: 1.3em;
opacity: 0.5;
/*transform-origin: left center;*/
}
.layout-2 .is-minimized .code-wrap__header {
writing-mode: vertical-lr;
padding: 10px 5px;
}
.code-wrap__header .caret {
transition: 0.2s ease;
}
.is-minimized .code-wrap__header .caret {
opacity: 0;
}
.code-wrap__header-btn {
width: 18px;
height: 18px;
display: inline-block;
}
.code-wrap__collapse-btn:before {
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" style="width:18px;height:18px" viewBox="0 0 24 24"><path fill="rgba(255,255,255,0.2)" d="M19.5,3.09L15,7.59V4H13V11H20V9H16.41L20.91,4.5L19.5,3.09M4,13V15H7.59L3.09,19.5L4.5,20.91L9,16.41V20H11V13H4Z" /></svg>');
}
.is-minimized .code-wrap__collapse-btn:before {
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" style="width:18px;height:18px" viewBox="0 0 24 24"><path fill="rgba(255,255,255,0.2)" d="M10,21V19H6.41L10.91,14.5L9.5,13.09L5,17.59V14H3V21H10M14.5,10.91L19,6.41V10H21V3H14V5H17.59L13.09,9.5L14.5,10.91Z" /></svg>');
}
@keyframes pop-in {
from { transform: scale(0.9); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
/* Codemirror */
.Codemirror {
width: 100%;
height: calc(100% - 25px); /* 25px for header */
font-size: 16px;
}
.layout-2 .is-minimized .Codemirror {
height: calc(100%);
}
.Codemirror pre {
font-family: 'Inconsolata', monospace;
}
.cm-s-monokai .CodeMirror-linenumber {
color:rgba(255,255,255,0.2);
}
.cm-s-monokai.CodeMirror,
.cm-s-monokai .CodeMirror-gutters {
background: var(--color-bg);
}
#demo-frame {
border: 0;
width: 100%;
height: 100%;
position: absolute;
z-index: 1;
background: white;
}
.main-header,
.footer {
padding: 5px 10px;
background-color: rgba(0, 0, 0, 0.5);
color: rgba(255, 255, 255, 0.45);
border-top: 1px solid rgba(255,255,255,0.14);
line-height: 20px;
}
.main-header {
border: 0;
border-bottom: 1px solid rgba(255,255,255,0.14);
}
.main-header__btn-wrap > a {
font-size: 0.8em;
color: #9297B3;
border-radius: 3px;
border: 1px solid rgba(146, 151, 179, 0.33);
margin-left: 10px;
padding: 0px 5px;
text-transform: uppercase;
}
.main-header__btn-wrap > a > svg {
fill: #9297B3;
margin-right: 4px;
}
.main-header__btn-wrap > a:hover {
border-color: rgba(146, 151, 179, 0.5);
}
.logo {
display: inline-block;
height: 25px;
width: 48px;
margin-right: 5px;
background: url(icon-48.png) 0px -12px;
background-repeat: no-repeat;
vertical-align: middle;
-webkit-filter: grayscale(0.9);
transition: 0.4s ease;
}
.footer:hover .logo {
-webkit-filter: grayscale(0);
}
.footer__right {
font-size: 0;
line-height: 0;
}
.footer__separator {
display: inline-block;
height: 24px;
margin: 0 10px 0 20px;
border-left: 1px solid rgba(255,255,255,0.2);
}
.mode-btn {
margin-left: 10px;
display: inline-block;
}
.footer__link:first-of-type {
margin-left: 5px;
}
.footer__link {
display: inline-block;
margin-right: 5px;
position: relative;
top: 2px;
}
.footer a > svg {
transition: 0.3s ease;
fill: rgba(255, 255, 255, 0.2)
}
.footer a:hover svg {
fill: rgba(255, 255, 255, 0.45)
}
.mode-btn svg {
width: 24px;
height: 24px;
}
.mode-btn.selected svg {
fill: rgba(255, 255, 255, 0.45);
}
.gutter {
background: rgba(255, 255, 255, 0.05);
flex-shrink: 0;
}
.gutter-horizontal {
cursor: ew-resize;
}
.gutter-vertical {
cursor: ns-resize;
}
.item-title-input {
background: none;
border: 0;
color: rgba(255,255,255,0.6);
width: calc(100vw - 400px);
}
.modal {
position: fixed;
top: 5vh;
left: 50%;
width: 60vw;
margin-left: -30vw;
max-width: 90vw;
height: auto;
z-index: 2000;
visibility: hidden;
}
@media screen and (max-width: 900px) {
.modal {
width: 90vw;
margin-left: -45vw;
}
}
.modal__content {
background: #fdfdfd;
color: #444;
position: relative;
border-radius: 3px;
margin: 0 auto;
opacity: 0;
padding: 2em;
font-size: 1.3em;
max-height: 82vh;
overflow-y: auto;
transition: all 0.19s;
transform: translateY(-50px) scale(0.7);
}
.is-modal-visible {
visibility: visible;
}
.is-modal-visible .modal__content {
transition-duration: 0.3s;
transform: translateY(0px) scale(1);
opacity: 1;
}
.modal-overlay {
position: fixed;
width: 100%;
height: 100%;
visibility: hidden;
top: 0;
left: 0;
z-index: 5;
opacity: 0;
will-change: opacity;
background: rgba(0,0,0,0.6);
transition: all 0.3s;
}
.saved-items-pane {
position: fixed;
right: 0;
top: 0;
bottom: 0;
width: 400px;
padding: 20px 30px;
z-index: 6;
background-color: var(--color-sidebar);
transition: 0.3s cubic-bezier(1, 0.13, 0.21, 0.87);
will-change: transform;
transform: translateX(100%);
}
.saved-items-pane.is-open {
transition-duration: 0.4s;
transform: translateX(0);
}
.is-modal-visible ~ .modal-overlay,
.saved-items-pane.is-open ~ .modal-overlay {
opacity: 1;
visibility: visible;
}
.saved-items-pane__close-btn {
position: absolute;
left: -18px;
top: 24px;
opacity: 0;
visibility: hidden;
border-radius: 50%;
padding: 10px 14px;
background: crimson;
transform: scale(0);
will-change: transform, opacity;
transition: 0.3s ease;
transition-delay: 0;
}
.saved-items-pane.is-open .saved-items-pane__close-btn {
opacity: 1;
transition-delay: 0.4s;
transform: scale(1);
visibility: visible;
}
.saved-item-tile {
padding: 20px;
background-color: rgba(255, 255, 255, 0.06);
position: relative;
/*border: 1px solid rgba(255,255,255,0.1);*/
margin: 20px 0;
display: block;
border-radius: 4px;
cursor: pointer;
opacity: 0;
transform: translateX(50px);
will-change: opacity, transform;
box-shadow: 0 2px 4px 0 rgba(0,0,0,0.2);
animation: slide-left 0.35s ease forwards;
}
.saved-item-tile:nth-child(1) { animation-delay: 0.2s; }
.saved-item-tile:nth-child(2) { animation-delay: 0.25s; }
.saved-item-tile:nth-child(3) { animation-delay: 0.3s; }
.saved-item-tile:nth-child(4) { animation-delay: 0.35s; }
.saved-item-tile:nth-child(5) { animation-delay: 0.4s; }
.saved-item-tile:nth-child(6) { animation-delay: 0.45s; }
.saved-item-tile:nth-child(7) { animation-delay: 0.5s; }
.saved-item-tile:nth-child(8) { animation-delay: 0.55s; }
.saved-item-tile:nth-child(9) { animation-delay: 0.6s; }
.saved-item-tile:nth-child(10) { animation-delay: 0.65s; }
.saved-item-tile:nth-child(11) { animation-delay: 0.7s; }
.saved-item-tile:nth-child(12) { animation-delay: 0.75s; }
.saved-item-tile:nth-child(n+12) { animation-delay: 0.8s; }
@keyframes slide-left {
from {
opacity: 0;
transform: translateX(50px);
}
to {
opacity: 1;
transform: translateX(0px);
}
}
.saved-item-tile:hover {
background: rgba(255,255,255,0.1);
}
.saved-item-tile__close-btn {
padding: 7px 10px;
position: absolute;
right: 8px;
top: 6px;
z-index: 1;
color: rgba(255,255,255,0.3);
border-radius: 50%;
background: rgba(255,255,255,0.05);
}
.saved-item-tile__close-btn:hover {
background: rgba(255,255,255,0.8);
color: #555;
}
.saved-item-tile__title {
font-size: 1.6em;
margin: 0 0 1em 0;
opacity: 0.8;
}
.saved-item-tile__meta {
opacity: 0.3;
}
.saved-items-pane__container {
overflow-y: scroll;
max-height: calc(100vh - 90px);
}
.notifications-btn {
position: relative;
}
@keyframes shake {
2%, 22% {
transform: translate3d(-1px, 0, 0);
}
5%,20% {
transform: translate3d(2px, 0, 0);
}
7%, 12%, 17% {
transform: translate3d(-4px, 0, 0);
}
10%, 15% {
transform: translate3d(4px, 0, 0);
}
}
.notifications-btn.has-new {
animation: shake 7s linear infinite;
transform-origin: 50% 10px;
}
.notifications-btn__dot {
position: absolute;
right: 1;
top: -2px;
background: #31fe45;
border-radius: 50%;
width: 12px;
height: 12px;
display: none;
}
.has-new .notifications-btn__dot {
display: block;
}
.notification {
border: 1px solid #f1f1f1;
border-radius: 5px;
padding: 20px;
background: #f8f6f9;
position: relative;
}
.notification:not(:last-child) {
margin-bottom: 10px;
}
.notification li:not(:last-child) {
margin-bottom: 10px;
}
.notification__version {
background: #ff8c00;
color: white;
padding: 3px;
border-radius: 5px;
position: absolute;
top: 2px;
left: 2px;
}
.btn-group {
position: relative;
cursor: pointer;
}
.dropdown__menu {
position: absolute;
top: 100%;
left: 0;
padding: 0;
margin: 0;
min-width: 200px;
display: block;
list-style: none;
border-radius: 4px;
overflow: hidden;
opacity: 0;
visibility: hidden;
transition: 0.25s ease;
transform: translateY(10px);
z-index: 5;
background: white;
}
.dropdown__menu > li > a {
display: block;
padding: 15px;
color: #333;
cursor: pointer;
}
.dropdown__menu > li > a:hover {
background: var(--color-sidebar);
color: white;
}
.dropdown__menu > li:not(:last-child) {
border-bottom: 1px solid rgba(0,0,0,0.05);
}
.open .dropdown__menu {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.alerts-container {
position: fixed;
will-change: transform;
left: 50%;
top: 0;
padding: 10px;
background: var(--color-sidebar);
border-radius: 3px;
z-index: 6;
transform: translateY(-100%);
transition: 0.3s ease;
}
.alerts-container.is-active {
transform: translateY(0);
}
.error-gutter {
width: 8px;
}
.gutter-error-marker {
width: 8px;
height: 20px;
background: red;
border-radius: 0;
position: relative;
top: 0;
left: 1px;
}
.gutter-error-marker:after {
content: attr(data-title);
background: red;
color: white;
padding: 4px;
opacity: 0;
visibility: hidden;
position: absolute;
top: 14px;
left: 0px;
white-space: nowrap;
transform: translateX(-10px);
will-change: transform;
transition: 0.2s ease;
}
.gutter-error-marker:hover:after {
opacity: 1;
visibility: visible;
transform: translateX(0);
}
.count-label {
color: rgba(0,0,0,0.8);
background: rgba(255,255,255,0.53);
border-radius: 5px;
padding: 1px 6px;
font-weight: bold;
}

109
src/utils.js Normal file
View File

@ -0,0 +1,109 @@
(function () {
window.DEBUG = document.cookie.indexOf('wmdebug') > -1;
window.$ = document.querySelector.bind(document);
window.$all = document.querySelectorAll.bind(document);
var alphaNum = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
// https://github.com/substack/semver-compare/blob/master/index.js
function semverCompare(a, b) {
var pa = a.split('.');
var pb = b.split('.');
for (var i = 0; i < 3; i++) {
var na = Number(pa[i]);
var nb = Number(pb[i]);
if (na > nb) { return 1; }
if (nb > na) { return -1; }
if (!isNaN(na) && isNaN(nb)) { return 1; }
if (isNaN(na) && !isNaN(nb)) { return -1; }
}
return 0;
}
function generateRandomId(len) {
var length = len || 10;
var id = '';
for (var i = length; i--;) {
id += alphaNum[~~(Math.random() * alphaNum.length)];
}
return id;
}
function onButtonClick(btn, listener) {
btn.addEventListener('click', function buttonClickListener(e) {
listener(e);
return false;
});
}
function log() {
if (window.DEBUG) {
console.log(...arguments);
}
}
// Generate 2 ASTs for the code to be inserted in loops for infinite run protection.
// The `myVar` variable names would be changed later for every insertion.
function getLoopProtectorBlocks() {
var ast1 = esprima.parse('var myvar = Date.now();');
var ast2 = esprima.parse('while(a){if (Date.now() - a787897 > 1000) { window.top.previewException(new Error("Infinite loop")); break;}}');
return {
before: ast1.body[0],
inside: ast2.body[0].body.body[0]
}
}
/**
* Add timed limit on the loops found in the passed AST body
* @param {ASTBody} Body of an AST generated by esprima or any ES compliant AST
*/
function addInfiniteLoopProtection(astBody) {
if (!astBody) { return; }
if (!Array.isArray(astBody)) {
addInfiniteLoopProtection(astBody.body);
return;
}
var el, randomVariableName, insertionBLocks;
for (var i = astBody.length; i--;) {
el = astBody[i];
if (el && el.type === 'ForStatement' || el.type === 'WhileStatement' || el.type === 'DoWhileStatement') {
randomVariableName = '_' + generateRandomId(3);
insertionBLocks = getLoopProtectorBlocks();
insertionBLocks.before.declarations[0].id.name = insertionBLocks.inside.test.left.right.name = randomVariableName;
// Insert time variable assignment
astBody.splice(i, 0, insertionBLocks.before);
// If the loop's body is a single statement, then convert it into a block statement
// so that we can insert our conditional break inside it.
if (!Array.isArray(el.body)) {
el.body = {
body: [ el.body ],
type: 'BlockStatement'
};
}
// Insert IfStatement
el.body.body.unshift(insertionBLocks.inside);
}
if (el.body) {
addInfiniteLoopProtection(el.body);
}
}
}
function getHumanDate(timestamp) {
var d = new Date(timestamp);
var retVal = d.getDate() + ' '
+ [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'][d.getMonth()] + ' '
+ d.getFullYear();
return retVal;
}
window.utils = {
semverCompare: semverCompare,
generateRandomId: generateRandomId,
onButtonClick: onButtonClick,
addInfiniteLoopProtection: addInfiniteLoopProtection,
getHumanDate: getHumanDate,
log: log
};
})();