mirror of
https://github.com/chinchang/web-maker.git
synced 2025-07-26 00:11:13 +02:00
add basic Js13kgames mode
This commit is contained in:
@@ -1,6 +1,41 @@
|
||||
import { h, Component } from 'preact';
|
||||
import { Button } from './common';
|
||||
|
||||
class JS13K extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const compoDate = new Date('August 13 2018 11:00 GMT');
|
||||
var now = new Date();
|
||||
var daysLeft;
|
||||
if (+compoDate > +now) {
|
||||
daysLeft = Math.floor((compoDate - now) / 1000 / 3600 / 24);
|
||||
}
|
||||
this.setState({
|
||||
daysLeft
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div class="flex flex-v-center">
|
||||
<img
|
||||
src="http://js13kgames.com/img/js13kgames.png"
|
||||
alt="JS13K Games logo"
|
||||
height="24"
|
||||
/>{' '}
|
||||
<div class="footer__js13k-text">{this.state.daysLeft} days to go</div>
|
||||
<div
|
||||
style={{
|
||||
color: this.props.codeSize > 10 ? 'crimson' : 'limegreen'
|
||||
}}
|
||||
>
|
||||
{(this.props.codeSize / 1024).toFixed(2)} KB/ 13KB
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default class Footer extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -15,7 +50,83 @@ export default class Footer extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div id="footer" class="footer">
|
||||
<div class="footer__right fr">
|
||||
<div>
|
||||
<a
|
||||
href="https://webmakerapp.com/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<div class="logo" />
|
||||
</a>
|
||||
©
|
||||
<span class="web-maker-with-tag">Web Maker</span>
|
||||
<Button
|
||||
onClick={this.props.helpBtnClickHandler}
|
||||
data-event-category="ui"
|
||||
data-event-action="helpButtonClick"
|
||||
class="footer__link hint--rounded hint--top-right"
|
||||
aria-label="Help"
|
||||
>
|
||||
<svg
|
||||
style="width:20px; height:20px; vertical-align:text-bottom"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path d="M15.07,11.25L14.17,12.17C13.45,12.89 13,13.5 13,15H11V14.5C11,13.39 11.45,12.39 12.17,11.67L13.41,10.41C13.78,10.05 14,9.55 14,9C14,7.89 13.1,7 12,7A2,2 0 0,0 10,9H8A4,4 0 0,1 12,5A4,4 0 0,1 16,9C16,9.88 15.64,10.67 15.07,11.25M13,19H11V17H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12C22,6.47 17.5,2 12,2Z" />
|
||||
</svg>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={this.props.keyboardShortcutsBtnClickHandler}
|
||||
data-event-category="ui"
|
||||
data-event-action="keyboardShortcutButtonClick"
|
||||
class="footer__link hint--rounded hint--top-right hide-on-mobile"
|
||||
aria-label="Keyboard shortcuts"
|
||||
>
|
||||
<svg
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
verticalAlign: 'text-bottom'
|
||||
}}
|
||||
>
|
||||
<use xlinkHref="#keyboard-icon" />
|
||||
</svg>
|
||||
</Button>
|
||||
<a
|
||||
class="footer__link hint--rounded hint--top-right"
|
||||
aria-label="Tweet about 'Web Maker'"
|
||||
href="http://twitter.com/share?url=https://webmakerapp.com/&text=Web Maker - A blazing fast %26 offline web playground! via @webmakerApp&related=webmakerApp&hashtags=web,frontend,playground,offline"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<svg
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
verticalAlign: 'text-bottom'
|
||||
}}
|
||||
>
|
||||
<use xlinkHref="#twitter-icon" />
|
||||
</svg>
|
||||
</a>
|
||||
<Button
|
||||
onClick={this.props.supportDeveloperBtnClickHandler}
|
||||
data-event-category="ui"
|
||||
data-event-action="supportDeveloperFooterBtnClick"
|
||||
class="footer__link ml-1 hint--rounded hint--top-right hide-on-mobile support-link"
|
||||
aria-label="Support the developer by pledging some amount"
|
||||
>
|
||||
Donate
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{this.props.prefs.isJs13kModeOn ? (
|
||||
<div class="flex">
|
||||
<JS13K codeSize={this.props.codeSize} />
|
||||
<button onClick={this.props.onJs13KBtnClick}>Help</button>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div class="footer__right">
|
||||
<button
|
||||
onClick={this.props.saveHtmlBtnClickHandler}
|
||||
id="saveHtmlBtn"
|
||||
@@ -150,72 +261,6 @@ export default class Footer extends Component {
|
||||
</svg>
|
||||
</Button>
|
||||
</div>
|
||||
<a
|
||||
href="https://webmakerapp.com/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<div class="logo" />
|
||||
</a>
|
||||
©
|
||||
<span class="web-maker-with-tag">Web Maker</span>
|
||||
<Button
|
||||
onClick={this.props.helpBtnClickHandler}
|
||||
data-event-category="ui"
|
||||
data-event-action="helpButtonClick"
|
||||
class="footer__link hint--rounded hint--top-right"
|
||||
aria-label="Help"
|
||||
>
|
||||
<svg
|
||||
style="width:20px; height:20px; vertical-align:text-bottom"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path d="M15.07,11.25L14.17,12.17C13.45,12.89 13,13.5 13,15H11V14.5C11,13.39 11.45,12.39 12.17,11.67L13.41,10.41C13.78,10.05 14,9.55 14,9C14,7.89 13.1,7 12,7A2,2 0 0,0 10,9H8A4,4 0 0,1 12,5A4,4 0 0,1 16,9C16,9.88 15.64,10.67 15.07,11.25M13,19H11V17H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12C22,6.47 17.5,2 12,2Z" />
|
||||
</svg>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={this.props.keyboardShortcutsBtnClickHandler}
|
||||
data-event-category="ui"
|
||||
data-event-action="keyboardShortcutButtonClick"
|
||||
class="footer__link hint--rounded hint--top-right hide-on-mobile"
|
||||
aria-label="Keyboard shortcuts"
|
||||
>
|
||||
<svg
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
verticalAlign: 'text-bottom'
|
||||
}}
|
||||
>
|
||||
<use xlinkHref="#keyboard-icon" />
|
||||
</svg>
|
||||
</Button>
|
||||
<a
|
||||
class="footer__link hint--rounded hint--top-right"
|
||||
aria-label="Tweet about 'Web Maker'"
|
||||
href="http://twitter.com/share?url=https://webmakerapp.com/&text=Web Maker - A blazing fast %26 offline web playground! via @webmakerApp&related=webmakerApp&hashtags=web,frontend,playground,offline"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<svg
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
verticalAlign: 'text-bottom'
|
||||
}}
|
||||
>
|
||||
<use xlinkHref="#twitter-icon" />
|
||||
</svg>
|
||||
</a>
|
||||
<Button
|
||||
onClick={this.props.supportDeveloperBtnClickHandler}
|
||||
data-event-category="ui"
|
||||
data-event-action="supportDeveloperFooterBtnClick"
|
||||
class="footer__link ml-1 hint--rounded hint--top-right hide-on-mobile support-link"
|
||||
aria-label="Support the developer by pledging some amount"
|
||||
>
|
||||
Support the developer
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
74
src/components/Js13KModal.jsx
Normal file
74
src/components/Js13KModal.jsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import { h } from 'preact';
|
||||
import Modal from './Modal';
|
||||
|
||||
export function Js13KModal({ show, closeHandler }) {
|
||||
return (
|
||||
<Modal show={show} closeHandler={closeHandler} small>
|
||||
<div class="tac">
|
||||
<div className="flex flex-v-center flex-h-center ">
|
||||
<img
|
||||
src="/icon-128.png"
|
||||
alt="Web Maker logo"
|
||||
height="100"
|
||||
style="margin:0 0.5rem;"
|
||||
/>
|
||||
<h2>Web Maker</h2>
|
||||
<span style="font-size:3rem;margin:0 1rem;">➕</span>
|
||||
<h2>JS13K Games</h2>
|
||||
<img
|
||||
src="http://js13kgames.com/img/logo.png"
|
||||
alt="JS13K Games logo"
|
||||
height="100"
|
||||
style="margin:0 0.5rem;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<strong>Js13kGames</strong> is a JavaScript coding competition for{' '}
|
||||
<strong>HTML5 Game Developers</strong>. The fun part of the compo is
|
||||
the file size limit set to <strong>13 kilobytes</strong>. The
|
||||
competition will start at <strong>13:00 CEST, 13th August</strong> and
|
||||
will end at <strong>13:00 CEST, 13th September 2018</strong>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Web Maker is an frontend playground that works offline! You can play
|
||||
around quickly with HTML, JavaScript & CSS to build your awesome game
|
||||
right here.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="https://gamedevelopment.tutsplus.com/articles/how-to-minify-your-html5-game-for-the-js13kgames-competition--cms-21883"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read Tuts+ Gamedev intro article
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="http://js13kgames.github.io/resources/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Resources and useful tools
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="http://2018.js13kgames.com/#rules"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Compo rules
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Have fun building games!</p>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@@ -56,7 +56,9 @@ export default class Modal extends Component {
|
||||
return (
|
||||
<Portal into="body">
|
||||
<div
|
||||
class={`${this.props.extraClasses || ''} modal is-modal-visible`}
|
||||
class={`${this.props.extraClasses || ''} modal is-modal-visible ${
|
||||
this.props.small ? 'modal--small' : ''
|
||||
}`}
|
||||
ref={el => (this.overlayEl = el)}
|
||||
onClick={this.onOverlayClick.bind(this)}
|
||||
>
|
||||
|
@@ -286,6 +286,14 @@ export default class Settings extends Component {
|
||||
pref={this.props.prefs.isCodeBlastOn}
|
||||
onChange={this.updateSetting.bind(this)}
|
||||
/>
|
||||
|
||||
<CheckboxSetting
|
||||
title="Get ready to build some games at JS13KGames"
|
||||
label="JS13KGames Mode"
|
||||
name="isJs13kModeOn"
|
||||
pref={this.props.prefs.isJs13kModeOn}
|
||||
onChange={this.updateSetting.bind(this)}
|
||||
/>
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
@@ -10,13 +10,15 @@ import SavedItemPane from './SavedItemPane.jsx';
|
||||
import AddLibrary from './AddLibrary.jsx';
|
||||
import Modal from './Modal.jsx';
|
||||
import Login from './Login.jsx';
|
||||
import { computeHtml, computeCss, computeJs } from '../computes';
|
||||
import {
|
||||
log,
|
||||
generateRandomId,
|
||||
semverCompare,
|
||||
saveAsHtml,
|
||||
handleDownloadsPermission,
|
||||
downloadFile
|
||||
downloadFile,
|
||||
getCompleteHtml
|
||||
} from '../utils';
|
||||
import { itemService } from '../itemService';
|
||||
import '../db';
|
||||
@@ -38,7 +40,9 @@ import { Alerts } from './Alerts';
|
||||
import Portal from 'preact-portal';
|
||||
import { HelpModal } from './HelpModal';
|
||||
import { OnboardingModal } from './OnboardingModal';
|
||||
import { Js13KModal } from './Js13KModal';
|
||||
import { Icons } from './Icons';
|
||||
import JSZip from 'jszip';
|
||||
|
||||
if (module.hot) {
|
||||
require('preact/debug');
|
||||
@@ -68,6 +72,7 @@ export default class App extends Component {
|
||||
isKeyboardShortcutsModalOpen: false,
|
||||
isAskToImportModalOpen: false,
|
||||
isOnboardModalOpen: false,
|
||||
isJs13KModalOpen: false,
|
||||
prefs: {},
|
||||
currentItem: {
|
||||
title: '',
|
||||
@@ -96,7 +101,8 @@ export default class App extends Component {
|
||||
lightVersion: false,
|
||||
lineWrap: true,
|
||||
infiniteLoopTimeout: 1000,
|
||||
layoutMode: 2
|
||||
layoutMode: 2,
|
||||
isJs13kModeOn: false
|
||||
};
|
||||
this.prefs = {};
|
||||
|
||||
@@ -493,7 +499,8 @@ export default class App extends Component {
|
||||
isSupportDeveloperModalOpen: false,
|
||||
isKeyboardShortcutsModalOpen: false,
|
||||
isAskToImportModalOpen: false,
|
||||
isOnboardModalOpen: false
|
||||
isOnboardModalOpen: false,
|
||||
isJs13KModalOpen: false
|
||||
});
|
||||
}
|
||||
onExternalLibChange(newValues) {
|
||||
@@ -697,6 +704,7 @@ export default class App extends Component {
|
||||
});
|
||||
}
|
||||
}
|
||||
this.calculateCodeSize();
|
||||
}
|
||||
onCodeSettingsChange(type, settings) {
|
||||
this.state.currentItem[`${type}Settings`] = {
|
||||
@@ -973,6 +981,138 @@ export default class App extends Component {
|
||||
this.state.currentItem.mainSizes = this.getMainPaneSizes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate byte size of a text snippet
|
||||
* @author Lea Verou
|
||||
* MIT License
|
||||
*/
|
||||
calculateTextSize(text) {
|
||||
if (!text) {
|
||||
return 0;
|
||||
}
|
||||
var crlf = /(\r?\n|\r)/g,
|
||||
whitespace = /(\r?\n|\r|\s+)/g;
|
||||
|
||||
const ByteSize = {
|
||||
count: function(text, options) {
|
||||
// Set option defaults
|
||||
options = options || {};
|
||||
options.lineBreaks = options.lineBreaks || 1;
|
||||
options.ignoreWhitespace = options.ignoreWhitespace || false;
|
||||
|
||||
var length = text.length,
|
||||
nonAscii = length - text.replace(/[\u0100-\uFFFF]/g, '').length,
|
||||
lineBreaks = length - text.replace(crlf, '').length;
|
||||
|
||||
if (options.ignoreWhitespace) {
|
||||
// Strip whitespace
|
||||
text = text.replace(whitespace, '');
|
||||
|
||||
return text.length + nonAscii;
|
||||
} else {
|
||||
return (
|
||||
length +
|
||||
nonAscii +
|
||||
Math.max(0, options.lineBreaks * (lineBreaks - 1))
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
format: function(count, plainText) {
|
||||
var level = 0;
|
||||
|
||||
while (count > 1024) {
|
||||
count /= 1024;
|
||||
level++;
|
||||
}
|
||||
|
||||
// Round to 2 decimals
|
||||
count = Math.round(count * 100) / 100;
|
||||
|
||||
level = ['', 'K', 'M', 'G', 'T'][level];
|
||||
|
||||
return (
|
||||
(plainText ? count : '<strong>' + count + '</strong>') +
|
||||
' ' +
|
||||
level +
|
||||
'B'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return ByteSize.count(text);
|
||||
}
|
||||
getExternalLibCode() {
|
||||
const item = this.state.currentItem;
|
||||
var libs = (item.externalLibs && item.externalLibs.js) || '';
|
||||
libs += ('\n' + item.externalLibs && item.externalLibs.css) || '';
|
||||
libs = libs.split('\n').filter(lib => lib);
|
||||
return libs.map(lib =>
|
||||
fetch(lib)
|
||||
.then(res => res.text())
|
||||
.then(data => {
|
||||
return {
|
||||
code: data,
|
||||
url: 'dsfds'
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
calculateCodeSize() {
|
||||
const item = this.state.currentItem;
|
||||
var htmlPromise = computeHtml(item.html, item.htmlMode);
|
||||
var cssPromise = computeCss(item.css, item.cssMode);
|
||||
var jsPromise = computeJs(item.js, item.jsMode, false);
|
||||
Promise.all([
|
||||
htmlPromise,
|
||||
cssPromise,
|
||||
jsPromise,
|
||||
...this.getExternalLibCode()
|
||||
]).then(result => {
|
||||
var html = result[0].code || '',
|
||||
css = result[1].code || '',
|
||||
js = result[2].code || '';
|
||||
|
||||
var fileContent = getCompleteHtml(html, css, js, item, true);
|
||||
|
||||
var zip = new JSZip();
|
||||
// Add an top-level, arbitrary text file with contents
|
||||
zip.file('index.html', fileContent);
|
||||
[result[3]].map(externalLib =>
|
||||
zip.file(externalLib.name, externalLib.code)
|
||||
);
|
||||
|
||||
console.log('ORIGINAL', this.calculateTextSize(fileContent));
|
||||
|
||||
var promise = null;
|
||||
if (0 && JSZip.support.uint8array) {
|
||||
promise = zip.generateAsync({ type: 'uint8array' });
|
||||
} else {
|
||||
promise = zip.generateAsync({
|
||||
type: 'string',
|
||||
compression: 'DEFLATE',
|
||||
compressionOptions: {
|
||||
level: 9
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
promise.then(data => {
|
||||
const zipContent = data;
|
||||
const size = this.calculateTextSize(data);
|
||||
this.setState({
|
||||
codeSize: size
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
js13KBtnClickHandler() {
|
||||
this.setState({
|
||||
isJs13KModalOpen: true
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
@@ -1006,6 +1146,7 @@ export default class App extends Component {
|
||||
/>
|
||||
<div class="global-console-container" id="globalConsoleContainerEl" />
|
||||
<Footer
|
||||
prefs={this.state.prefs}
|
||||
layoutBtnClickHandler={this.layoutBtnClickHandler.bind(this)}
|
||||
helpBtnClickHandler={() => this.setState({ isHelpModalOpen: true })}
|
||||
settingsBtnClickHandler={() =>
|
||||
@@ -1028,7 +1169,9 @@ export default class App extends Component {
|
||||
screenshotBtnClickHandler={this.screenshotBtnClickHandler.bind(
|
||||
this
|
||||
)}
|
||||
onJs13KBtnClick={this.js13KBtnClickHandler.bind(this)}
|
||||
hasUnseenChangelog={this.state.hasUnseenChangelog}
|
||||
codeSize={this.state.codeSize}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1145,6 +1288,11 @@ export default class App extends Component {
|
||||
closeHandler={() => this.setState({ isOnboardModalOpen: false })}
|
||||
/>
|
||||
|
||||
<Js13KModal
|
||||
show={this.state.isJs13KModalOpen}
|
||||
closeHandler={() => this.setState({ isJs13KModalOpen: false })}
|
||||
/>
|
||||
|
||||
<Portal into="body">
|
||||
<div
|
||||
class="modal-overlay"
|
||||
|
@@ -573,6 +573,8 @@ body > #demo-frame {
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
/* Because .console is 6 */
|
||||
z-index: 6;
|
||||
}
|
||||
@@ -852,7 +854,8 @@ body > #demo-frame {
|
||||
}
|
||||
|
||||
.pledge-modal .modal__content,
|
||||
.ask-to-import-modal .modal__content {
|
||||
.ask-to-import-modal .modal__content,
|
||||
.modal--small .modal__content {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
@@ -1506,6 +1509,16 @@ body:not(.is-app) .show-when-app {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.footer__js13k-text {
|
||||
padding: 1px 9px;
|
||||
text-transform: uppercase;
|
||||
background: #b12a34;
|
||||
color: white;
|
||||
border-radius: 3px;
|
||||
letter-spacing: 0.7px;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
body {
|
||||
font-size: 70%;
|
||||
|
@@ -378,7 +378,7 @@ export function saveAsHtml(item) {
|
||||
var htmlPromise = computeHtml(item.html, item.htmlMode);
|
||||
var cssPromise = computeCss(item.css, item.cssMode);
|
||||
var jsPromise = computeJs(item.js, item.jsMode, false);
|
||||
Promise.all([htmlPromise, cssPromise, jsPromise]).then(function(result) {
|
||||
Promise.all([htmlPromise, cssPromise, jsPromise]).then(result => {
|
||||
var html = result[0].code,
|
||||
css = result[1].code,
|
||||
js = result[2].code;
|
||||
|
Reference in New Issue
Block a user