mirror of
https://github.com/chinchang/web-maker.git
synced 2025-07-29 09:40:10 +02:00
so much more stuff ported
This commit is contained in:
@@ -17,3 +17,76 @@ export const JsModes = {
|
||||
COFFEESCRIPT: 'coffee',
|
||||
TS: 'typescript'
|
||||
};
|
||||
export const modes = {};
|
||||
modes[HtmlModes.HTML] = {
|
||||
label: 'HTML',
|
||||
cmMode: 'htmlmixed',
|
||||
codepenVal: 'none'
|
||||
};
|
||||
modes[HtmlModes.MARKDOWN] = {
|
||||
label: 'Markdown',
|
||||
cmMode: 'markdown',
|
||||
codepenVal: 'markdown'
|
||||
};
|
||||
modes[HtmlModes.JADE] = {
|
||||
label: 'Pug',
|
||||
cmMode: 'pug',
|
||||
codepenVal: 'pug'
|
||||
};
|
||||
modes[JsModes.JS] = {
|
||||
label: 'JS',
|
||||
cmMode: 'javascript',
|
||||
codepenVal: 'none'
|
||||
};
|
||||
modes[JsModes.COFFEESCRIPT] = {
|
||||
label: 'CoffeeScript',
|
||||
cmMode: 'coffeescript',
|
||||
codepenVal: 'coffeescript'
|
||||
};
|
||||
modes[JsModes.ES6] = {
|
||||
label: 'ES6 (Babel)',
|
||||
cmMode: 'jsx',
|
||||
codepenVal: 'babel'
|
||||
};
|
||||
modes[JsModes.TS] = {
|
||||
label: 'TypeScript',
|
||||
cmPath: 'jsx',
|
||||
cmMode: 'text/typescript-jsx',
|
||||
codepenVal: 'typescript'
|
||||
};
|
||||
modes[CssModes.CSS] = {
|
||||
label: 'CSS',
|
||||
cmPath: 'css',
|
||||
cmMode: 'css',
|
||||
codepenVal: 'none'
|
||||
};
|
||||
modes[CssModes.SCSS] = {
|
||||
label: 'SCSS',
|
||||
cmPath: 'css',
|
||||
cmMode: 'text/x-scss',
|
||||
codepenVal: 'scss'
|
||||
};
|
||||
modes[CssModes.SASS] = {
|
||||
label: 'SASS',
|
||||
cmMode: 'sass',
|
||||
codepenVal: 'sass'
|
||||
};
|
||||
modes[CssModes.LESS] = {
|
||||
label: 'LESS',
|
||||
cmPath: 'css',
|
||||
cmMode: 'text/x-less',
|
||||
codepenVal: 'less'
|
||||
};
|
||||
modes[CssModes.STYLUS] = {
|
||||
label: 'Stylus',
|
||||
cmMode: 'stylus',
|
||||
codepenVal: 'stylus'
|
||||
};
|
||||
modes[CssModes.ACSS] = {
|
||||
label: 'Atomic CSS',
|
||||
cmPath: 'css',
|
||||
cmMode: 'css',
|
||||
codepenVal: 'notsupported',
|
||||
cmDisable: true,
|
||||
hasSettings: true
|
||||
};
|
||||
|
99
webmaker/src/components/AddLibrary.jsx
Normal file
99
webmaker/src/components/AddLibrary.jsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import { h, Component } from 'preact';
|
||||
import { jsLibs, cssLibs } from '../libraryList';
|
||||
|
||||
export default class AddLibrary extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
css: props.css || '',
|
||||
js: props.js || ''
|
||||
};
|
||||
}
|
||||
onSelectChange(e) {
|
||||
const target = e.target;
|
||||
if (!target.value) {
|
||||
return;
|
||||
}
|
||||
const type = target.selectedOptions[0].dataset.type;
|
||||
if (type === 'js') {
|
||||
this.setState({
|
||||
js: `${this.state.js}\n${target.value}`
|
||||
});
|
||||
}
|
||||
|
||||
// trackEvent('ui', 'addLibrarySelect', target.selectedOptions[0].label);
|
||||
this.props.onChange({ js: this.state.js, css: this.state.css });
|
||||
// Reset the select to the default value
|
||||
target.value = '';
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Add Library</h1>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
id="externalLibrarySearchInput"
|
||||
class="full-width"
|
||||
placeholder="Type here to search libraries"
|
||||
/>
|
||||
<div class="tar opacity--70">
|
||||
<small>Powered by cdnjs</small>
|
||||
</div>
|
||||
<div style="margin:20px 0;">
|
||||
Choose from popular libraries:
|
||||
<select
|
||||
name=""
|
||||
id="js-add-library-select"
|
||||
onChange={this.onSelectChange.bind(this)}
|
||||
>
|
||||
<option value="">-------</option>
|
||||
<optgroup label="JavaScript Libraries">
|
||||
{jsLibs.map(lib => (
|
||||
<option data-type={lib.type} value={lib.url}>
|
||||
{lib.label}
|
||||
</option>
|
||||
))}
|
||||
</optgroup>
|
||||
<optgroup label="CSS Libraries">
|
||||
{cssLibs.map(lib => (
|
||||
<option data-type={lib.type} value={lib.url}>
|
||||
{lib.label}
|
||||
</option>
|
||||
))}
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<h3>JavaScript</h3>
|
||||
<p style="font-size: 0.8em;" class="show-when-extension opacity--70">
|
||||
Note: You can load external scripts from following domains: localhost,
|
||||
https://ajax.googleapis.com, https://code.jquery.com,
|
||||
https://cdnjs.cloudflare.com, https://unpkg.com, https://maxcdn.com,
|
||||
https://cdn77.com, https://maxcdn.bootstrapcdn.com,
|
||||
https://cdn.jsdelivr.net/, https://rawgit.com, https://wzrd.in
|
||||
</p>
|
||||
|
||||
<textarea
|
||||
onBlur={this.props.onChange}
|
||||
class="full-width"
|
||||
id=""
|
||||
cols="30"
|
||||
rows="5"
|
||||
placeholder="Start typing name of a library. Put each library in new line"
|
||||
value={this.state.js}
|
||||
/>
|
||||
|
||||
<h3>CSS</h3>
|
||||
<textarea
|
||||
onBlur={this.props.onChange}
|
||||
class="full-width"
|
||||
id=""
|
||||
cols="30"
|
||||
rows="5"
|
||||
placeholder="Start typing name of a library. Put each library in new line"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@@ -16,22 +16,23 @@ export default class ContentWrap extends Component {
|
||||
this.jsMode = JsModes.JS;
|
||||
this.prefs = {};
|
||||
this.codeInPreview = { html: null, css: null, js: null };
|
||||
this.cmCodes = { html: '', css: '', js: '' };
|
||||
}
|
||||
getInitialState() {
|
||||
return {};
|
||||
this.cmCodes = { html: props.currentItem.html, css: '', js: '' };
|
||||
this.cm = {};
|
||||
}
|
||||
|
||||
onHtmlCodeChange(editor, change) {
|
||||
this.cmCodes.html = editor.getValue();
|
||||
this.props.onCodeChange('html', this.cmCodes.html);
|
||||
this.onCodeChange(editor, change);
|
||||
}
|
||||
onCssCodeChange(editor, change) {
|
||||
this.cmCodes.css = editor.getValue();
|
||||
this.props.onCodeChange('css', this.cmCodes.css);
|
||||
this.onCodeChange(editor, change);
|
||||
}
|
||||
onJsCodeChange(editor, change) {
|
||||
this.cmCodes.js = editor.getValue();
|
||||
this.props.onCodeChange('js', this.cmCodes.js);
|
||||
this.onCodeChange(editor, change);
|
||||
}
|
||||
onCodeChange(editor, change) {
|
||||
@@ -287,16 +288,16 @@ export default class ContentWrap extends Component {
|
||||
this.codeInPreview.css = currentCode.css;
|
||||
this.codeInPreview.js = currentCode.js;
|
||||
}
|
||||
componentWillReceiveProps() {
|
||||
// console.log('compoenntwillrecvprops', this.props.currentItem);
|
||||
}
|
||||
componentDidUpdate() {
|
||||
this.cmCodes.html = this.props.currentItem.html;
|
||||
this.cmCodes.css = this.props.currentItem.css;
|
||||
this.cmCodes.js = this.props.currentItem.js;
|
||||
this.cm.html.setValue(this.cmCodes.html);
|
||||
this.cm.css.setValue(this.cmCodes.css);
|
||||
this.cm.js.setValue(this.cmCodes.js);
|
||||
this.setPreviewContent(true);
|
||||
console.log('componentdidupdate', this.props.currentItem);
|
||||
}
|
||||
componentWillUpdate() {
|
||||
// console.log('compoenntwillupdate', this.props.currentItem);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
@@ -343,6 +344,7 @@ export default class ContentWrap extends Component {
|
||||
matchTags: { bothTags: true }
|
||||
}}
|
||||
onChange={this.onHtmlCodeChange.bind(this)}
|
||||
onCreation={el => (this.cm.html = el)}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
@@ -402,6 +404,7 @@ export default class ContentWrap extends Component {
|
||||
// emmet: true
|
||||
}}
|
||||
onChange={this.onCssCodeChange.bind(this)}
|
||||
onCreation={el => (this.cm.css = el)}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
@@ -444,6 +447,7 @@ export default class ContentWrap extends Component {
|
||||
noAutocomplete: true
|
||||
}}
|
||||
onChange={this.onJsCodeChange.bind(this)}
|
||||
onCreation={el => (this.cm.js = el)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -120,6 +120,7 @@ export default class Footer extends Component {
|
||||
<div class="footer__separator" />
|
||||
|
||||
<a
|
||||
onClick={this.props.notificationsBtnClickHandler}
|
||||
id="notificationsBtn"
|
||||
class="notifications-btn mode-btn hint--top-left hint--rounded"
|
||||
aria-label="Notifications"
|
||||
@@ -130,13 +131,15 @@ export default class Footer extends Component {
|
||||
<span class="notifications-btn__dot" />
|
||||
</a>
|
||||
<a
|
||||
d-open-modal="settingsModal"
|
||||
onClick={this.props.settingsBtnClickHandler}
|
||||
data-event-category="ui"
|
||||
data-event-action="settingsBtnClick"
|
||||
class="mode-btn hint--top-left hint--rounded"
|
||||
aria-label="Settings"
|
||||
>
|
||||
Settings
|
||||
<svg>
|
||||
<use xlinkHref="#settings-icon" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<a href="https://webmakerapp.com/" target="_blank">
|
||||
|
158
webmaker/src/components/HelpModal.jsx
Normal file
158
webmaker/src/components/HelpModal.jsx
Normal file
@@ -0,0 +1,158 @@
|
||||
import { h, Component } from 'preact';
|
||||
import Modal from './Modal.jsx';
|
||||
|
||||
export default class Header extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Modal show={this.props.show} closeHandler={this.props.closeHandler}>
|
||||
<h1>
|
||||
<div class="web-maker-with-tag">Web Maker</div>
|
||||
<small style="font-size:14px;"> v3.2.0</small>
|
||||
</h1>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
Made with
|
||||
<span style="margin-right: 8px;">💖</span> &
|
||||
<span style="margin-right: 8px;">🙌</span> by
|
||||
<a href="https://twitter.com/chinchang457" target="_blank">
|
||||
Kushagra Gour
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="/docs" target="_blank">
|
||||
Read the documentation
|
||||
</a>.
|
||||
</p>
|
||||
<p>
|
||||
Tweet out your feature requests, comments & suggestions to
|
||||
<a target="_blank" href="https://twitter.com/webmakerApp">
|
||||
@webmakerApp
|
||||
</a>.
|
||||
</p>
|
||||
<p>
|
||||
Like this extension? Please
|
||||
<a
|
||||
href="https://chrome.google.com/webstore/detail/web-maker/lkfkkhfhhdkiemehlpkgjeojomhpccnh/reviews"
|
||||
target="_blank"
|
||||
>
|
||||
rate it here
|
||||
</a>.
|
||||
</p>
|
||||
<p>
|
||||
<button
|
||||
aria-label="Support the developer"
|
||||
d-click="openSupportDeveloperModal"
|
||||
data-event-action="supportDeveloperHelpBtnClick"
|
||||
class="btn btn-icon"
|
||||
>
|
||||
<svg>
|
||||
<use xlinkHref="#gift-icon" />
|
||||
</svg>Support the developer
|
||||
</button>
|
||||
<a
|
||||
aria-label="Rate Web Maker"
|
||||
href="https://chrome.google.com/webstore/detail/web-maker/lkfkkhfhhdkiemehlpkgjeojomhpccnh/reviews"
|
||||
target="_blank"
|
||||
class="btn btn-icon"
|
||||
>
|
||||
<svg>
|
||||
<use xlinkHref="#heart-icon" />
|
||||
</svg>Share Web Maker
|
||||
</a>
|
||||
<a
|
||||
aria-label="Chat"
|
||||
href="https://web-maker.slack.com"
|
||||
target="_blank"
|
||||
class="btn btn-icon"
|
||||
>
|
||||
<svg>
|
||||
<use xlinkHref="#chat-icon" />
|
||||
</svg>Chat
|
||||
</a>
|
||||
<a
|
||||
aria-label="Report a Bug"
|
||||
href="https://github.com/chinchang/web-maker/issues"
|
||||
target="_blank"
|
||||
class="btn btn-icon"
|
||||
>
|
||||
<svg>
|
||||
<use xlinkHref="#bug-icon" />
|
||||
</svg>Report a bug
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<h3>Awesome libraries used</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<a target="_blank" href="https://kushagragour.in/lab/hint/">
|
||||
Hint.css
|
||||
</a>{' '}
|
||||
&
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://github.com/chinchang/screenlog.js"
|
||||
>
|
||||
Screenlog.js
|
||||
</a>{' '}
|
||||
- By me :)
|
||||
</li>
|
||||
<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/enjalot/Inlet">
|
||||
Inlet
|
||||
</a>{' '}
|
||||
- Ian Johnson
|
||||
</li>
|
||||
<li>
|
||||
<a target="_blank" href="https://webmakerapp.com/">
|
||||
Web Maker!
|
||||
</a>{' '}
|
||||
- whhat!
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<h3>License</h3>
|
||||
"Web Maker" is
|
||||
<a target="_blank" href="https://github.com/chinchang/web-maker">
|
||||
open-source
|
||||
</a>{' '}
|
||||
under the
|
||||
<a href="https://opensource.org/licenses/MIT" target="_blank">
|
||||
MIT License
|
||||
</a>.
|
||||
</p>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
783
webmaker/src/components/Notifications.jsx
Normal file
783
webmaker/src/components/Notifications.jsx
Normal file
@@ -0,0 +1,783 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
export default class Notifications extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Whats new?</h1>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">3.1.1</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Bugfix</strong>: Fix the "Run" button not refreshing the
|
||||
preview after release 3.0.4.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">3.1.0</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Mobile Support (app only).</strong>: Make the Web Maker
|
||||
app usable on mobile. This is only for web app as Chrome
|
||||
extensions don't run on mobile.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="notification">
|
||||
<span class="notification__version">3.0.4</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Bugfix</strong>: Guarantee code doesn't execute when "auto
|
||||
preview" is off.
|
||||
</li>
|
||||
<li>
|
||||
Add link to our new
|
||||
<a href="https://web-maker.slack.com" target="_blank">
|
||||
Slack channel
|
||||
</a>{' '}
|
||||
🤗.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">3.0.3</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Bugfix (extension)</strong>: "Save as HTML" file saves
|
||||
with correct extension.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">3.0.1</span>
|
||||
<ul>
|
||||
<li>
|
||||
After months of work, here is Web Maker 3.0.
|
||||
<a
|
||||
href="https://medium.com/web-maker/web-maker-3-0-is-here-f158a40eeaee"
|
||||
target="_blank"
|
||||
>
|
||||
Read the blog post about it
|
||||
</a>.
|
||||
</li>
|
||||
<li>
|
||||
Web Maker is no more just a Chrome extension, it is also available
|
||||
as web app that runs offline just like the extension! Checkout it
|
||||
out ->
|
||||
<a href="https://webmakerapp.com/app/" target="_blank">
|
||||
https://webmakerapp.com/app/
|
||||
</a>.
|
||||
</li>
|
||||
<li>
|
||||
Now use Web Maker web app on any modern browser (tested with
|
||||
Chrome and Firefox).
|
||||
</li>
|
||||
<li>
|
||||
<strong>User Accounts</strong> - The much requested user accounts
|
||||
are here. Now maintain your account and store all your creations
|
||||
in the cloud and access them anywhere anytime.
|
||||
</li>
|
||||
<li>
|
||||
<strong>New layout mode</strong> - One more layout mode, that lets
|
||||
you align all the panes vertically.
|
||||
</li>
|
||||
<li>
|
||||
<strong>No more restriction on scripts (Web app only)</strong> -
|
||||
If you are using the web app, there is no more a restriction to
|
||||
load scripts from only specific domains. Load any script!
|
||||
</li>
|
||||
<li>
|
||||
<strong>Inline scripts (Web app only)</strong> - The restriction
|
||||
of writing JavaScript only in JS pane is also removed.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.9.7</span>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://tailwindcss.com/" target="_blank">
|
||||
Tailwind CSS
|
||||
</a>{' '}
|
||||
added to popular CSS libraries list. Thanks
|
||||
<a href="https://github.com/diomed" target="_blank">
|
||||
diomed
|
||||
</a>.
|
||||
</li>
|
||||
<li>
|
||||
Popular libraries list updated. Thanks
|
||||
<a href="https://github.com/diomed" target="_blank">
|
||||
diomed
|
||||
</a>.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Dev</strong>: Bug fixes and code refactoring to make
|
||||
things simple. Thanks
|
||||
<a href="https://github.com/iamandrewluca" target="_blank">
|
||||
iamandrewluca
|
||||
</a>.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.9.6</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Bugfix</strong>: Fix close buttons not working in
|
||||
notifications and keyboard shortcuts modal.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong>: Fix keyboard shortcut to see keyboard
|
||||
shortcuts :) Thanks
|
||||
<a href="https://github.com/ClassicOldSong" target="_blank">
|
||||
ClassicOldSong
|
||||
</a>.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.9.5</span>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="https://medium.com/web-maker/release-2-9-5-add-library-search-pane-collapsing-ux-improvements-more-1085216c1301"
|
||||
target="_blank"
|
||||
>
|
||||
Read blog post about this release.
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Keyboard shortcuts panel</strong>: Add a list of all
|
||||
keyboard shotcuts. Access with
|
||||
<code> Ctrl/⌘ + Shift + ?</code> or click keyboard button in
|
||||
footer.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Add external library</strong>: Better UX for searching
|
||||
third party libraries.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Improvement</strong>: Code panes now go fullscreen when
|
||||
double-clicked on their headers - which is much more intuitive
|
||||
behavior based on feedback from lot of developers.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Improvement</strong>: Add
|
||||
<code>allowfullscreen</code> attribute on iframes. Thanks
|
||||
<a href="https://github.com/ClassicOldSong" target="_blank">
|
||||
ClassicOldSong
|
||||
</a>.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - Stop screenlog.js from showing up in the
|
||||
exported HTML.
|
||||
</li>
|
||||
<li>
|
||||
Popular external libraries list updated. Thanks
|
||||
<a href="https://github.com/jlapitan" target="_blank">
|
||||
jlapitan
|
||||
</a>.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.9.4</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Improvement</strong>: Atomic CSS (Atomizer) has been
|
||||
updated to latest version. Now you can do things like psuedo
|
||||
elements. Learn More.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - Logging circular objects is now
|
||||
possible. It won't show in the Web Maker console, but will show
|
||||
fine in browser's console.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - Console's z-index issue has been fixed.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.9.3</span>
|
||||
<ul>
|
||||
<li>
|
||||
Choose the save location while exporting your saved creations. Now
|
||||
easily sync them to your Dropbox or any cloud storage.
|
||||
</li>
|
||||
<li>All modals inside the app now have a close button.</li>
|
||||
<li>
|
||||
Checkbox that showed on clicking a boolean value is now removed.
|
||||
Thanks
|
||||
<a href="https://github.com/gauravmuk" target="_blank">
|
||||
Gaurav Nanda
|
||||
</a>.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - Screenshots on retina device are now
|
||||
correct. Thanks
|
||||
<a href="https://github.com/AshBardhan" target="_blank">
|
||||
Ashish Bardhan
|
||||
</a>.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - Double console log in detached mode
|
||||
fixed.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - Console.clear now works in detached mode
|
||||
too.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - DOCTYPE added in preview.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - Typo correction in README. Thanks
|
||||
<a href="https://github.com/AdilMah" target="_blank">
|
||||
Adil Mahmood
|
||||
</a>.
|
||||
</li>
|
||||
<li>gstatic.com is available to load external JavaScripts from.</li>
|
||||
<li>
|
||||
Popular libraries list updated. Thanks
|
||||
<a href="https://github.com/diomed" target="_blank">
|
||||
diomed
|
||||
</a>.
|
||||
</li>
|
||||
<li>
|
||||
Added
|
||||
<a
|
||||
href="https://github.com/chinchang/web-maker/blob/master/CONTRIBUTING.md"
|
||||
target="_blank"
|
||||
>
|
||||
contribution guidelines
|
||||
</a>{' '}
|
||||
in the Github repository.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.9.2</span>
|
||||
<ul>
|
||||
<li>Minor bug fixes.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.9.1</span>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="https://medium.com/web-maker/v2-9-lots-of-goodies-bd1e939571f6"
|
||||
target="_blank"
|
||||
>
|
||||
Read blog post about last release.
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
Use Ctrl/Cmd+D to select next occurence of matching selection.
|
||||
</li>
|
||||
<li>Improve onboard experience.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.9.0</span>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="https://medium.com/web-maker/v2-9-lots-of-goodies-bd1e939571f6"
|
||||
target="_blank"
|
||||
>
|
||||
Read blog post about this release.
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Detached Preview</strong> - Yes, you read that correct!
|
||||
You can now detach your preview and send it to your secondary
|
||||
monitor.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Find & Replace</strong> - Long awaited, now its there.
|
||||
Ctrl/Cmd+f to find and add Alt to replace.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Atomic CSS (Atomizer) configurations</strong> - Add custom
|
||||
config for Atomic CSS.
|
||||
<a href="https://github.com/acss-io/atomizer#api" target="_blank">
|
||||
Read more
|
||||
</a>.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Light mode</strong> - This new setting makes Web Maker
|
||||
drop some heavy effects like blur etc to gain more performance.
|
||||
Thanks
|
||||
<a href="https://github.com/iamandrewluca" target="_blank">
|
||||
Andrew
|
||||
</a>.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Preserve logs setting</strong> - Choose whether or not to
|
||||
preserve logs across preview refreshes. Thanks
|
||||
<a href="https://github.com/BasitAli" target="_blank">
|
||||
Basit
|
||||
</a>.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Line wrap setting</strong> - As the name says.
|
||||
</li>
|
||||
<li>Semantic UI added to popular libraries.</li>
|
||||
<li>
|
||||
Bootstrap, Vue, UI-Kit and more updated to latest versions in
|
||||
popular libraries.
|
||||
</li>
|
||||
<li>UX improvements in settings UI</li>
|
||||
|
||||
<li>
|
||||
<strong>Bugfix</strong> - Trigger preview refresh anytime with
|
||||
Ctrl/⌘ + Shift + 5. Even with auto-preview on.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.8.1</span>
|
||||
<ul>
|
||||
<li>
|
||||
Vue.js & UIKit version updated to latest version in 'Add Library'
|
||||
list.
|
||||
</li>
|
||||
<li>
|
||||
UTF-8 charset added to preview HTML to support universal
|
||||
characters.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.8.0</span>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="https://medium.com/web-maker/release-v2-8-is-out-f44e6ea5d9c4"
|
||||
target="_blank"
|
||||
>
|
||||
Read blog post about this release.
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Auto Save</strong> - Your creations now auto-save after
|
||||
your first manual save. This is configurable from settings.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Base2Tone-Meadow Editor Theme</strong> - First user
|
||||
contributed theme. Thanks to Diomed.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Use System Fonts</strong> - You can now use any of your
|
||||
existing system fonts in the editor!
|
||||
</li>
|
||||
<li>
|
||||
<strong>Matching Tag Highlight</strong> - Cursor over any HTML tag
|
||||
would highlight the matching pair tag.
|
||||
</li>
|
||||
<li>
|
||||
Auto-completion suggestion can now be switched off from settings.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Improvement</strong> - Stop white flicker in editor when
|
||||
the app opens.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - Add Babel Polyfill to enable use of
|
||||
next-gen built-ins like Promise or WeakMap.
|
||||
</li>
|
||||
<li>Vue.js version updated to 2.4.0 in popular library list.</li>
|
||||
<li>
|
||||
Downloads permission is optional. Asked only when you take
|
||||
screenshot.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.7.2</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>External Libraries</strong> - Add Foundation.js and update
|
||||
UIKit 3 to latest beta.
|
||||
</li>
|
||||
<li>
|
||||
<strong>rawgit.com</strong> &
|
||||
<strong>wzrd.in</strong> domains are now allowed for loading
|
||||
external libraries from.
|
||||
</li>
|
||||
<li>Minor booting speed improvements</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.7.1</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Framer.js support</strong> - You can now load the latest
|
||||
framer.js library from
|
||||
<a href="https://builds.framerjs.com/" target="_blank">
|
||||
framer builds page
|
||||
</a>{' '}
|
||||
and start coding framer prototypes.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong>: Edit on CodePen is back in action.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong>: Autocompletion menu doesn't show on cut
|
||||
and paste now.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong>: Updated & fixed urls of some common
|
||||
external libraries to latest versions. UIKit3 & Bootstrap 4α are
|
||||
now in the list.
|
||||
</li>
|
||||
<li>Preprocessor selector are now more accessible.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.7.0</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Fork any creation!</strong>: Now you can fork any existing
|
||||
creation of yours to start a new work based on it. One big use
|
||||
case of this feature is "Templates"!
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://kushagragour.in/blog/2017/05/web-maker-fork-templates/?utm_source=webmakerapp&utm_medium=referral"
|
||||
>
|
||||
Read more about it
|
||||
</a>.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Fonts 😍 </strong>: Super-awesome 4 fonts (mostly with
|
||||
ligature support) now available to choose from. Fira Code is the
|
||||
default font now.
|
||||
</li>
|
||||
<li>Updated most used external libraries to latest versions.</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong>: Add missing Bootstrap JS file to most
|
||||
used external libraries list.
|
||||
</li>
|
||||
<li>
|
||||
Several other minor bugfixes and improvements to make Web Maker
|
||||
awesome!
|
||||
</li>
|
||||
|
||||
<li>
|
||||
Great news to share with you - Web Maker has been featured on the
|
||||
Chrome Webstore homepage! Thanks for all the love :)
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.6.1</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Bugfix</strong>: Emojis vanishing while exporting to
|
||||
Codepen has been fixed.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong>:
|
||||
<code>console.clear()</code> now doesn't error and clears the
|
||||
inbuilt console.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong>: External libraries added to the creation
|
||||
are exported as well to Codepen.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.6.0</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>The "Console"</strong>: The most awaited feature is here!
|
||||
There is now an inbuilt console to see your logs, errors and for
|
||||
quickly evaluating JavaScript code inside your preview. Enjoy! I
|
||||
also a
|
||||
<a
|
||||
href="https://kushagragour.in/blog/2017/05/web-maker-console-is-here/?utm_source=webmakerapp&utm_medium=referral"
|
||||
target="_blank"
|
||||
>
|
||||
blog post about it
|
||||
</a>.
|
||||
</li>
|
||||
<li>
|
||||
Number slider which popped on clicking any number in the code has
|
||||
been removed due to poor user experience.
|
||||
</li>
|
||||
<li>Minor usability improvements.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.5.0</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Atomic CSS</strong>: Use can now use Atomic CSS(ACSS) in
|
||||
your work!
|
||||
<a href="https://acss.io/" target="_blank">
|
||||
Read more about it here
|
||||
</a>.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Search your saved creations</strong>: Easily search
|
||||
through all your saved creations by title.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Configurable Auto-preview</strong> - You can turn off the
|
||||
auto preview in settings if you don't want the preview to update
|
||||
as you type.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Configurable refresh on resize</strong> - You can
|
||||
configure whether you want the preview to refresh when you resize
|
||||
the preview panel.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - Fix indentation
|
||||
<a
|
||||
href="https://github.com/chinchang/web-maker/issues/104"
|
||||
target="_blank"
|
||||
>
|
||||
issue
|
||||
</a>{' '}
|
||||
with custom indentation size.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.4.2</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Improved infinite loop protection</strong>: Infinite loop
|
||||
protection is now faster and more reliable. And works without the
|
||||
need of Escodegen. Thanks to Ariya Hidayat!
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - Default parameters not working in
|
||||
JavaScript is fixed.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.4.0</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Import/Export</strong>: Your creations are most important.
|
||||
Now export all your creations into a single file as a backup that
|
||||
can be imported anytime & anywhere.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Editor themes</strong>: You have been heard. Now you can
|
||||
choose from a huge list of wonderful editor themes!
|
||||
</li>
|
||||
<li>
|
||||
<strong>Identation settings</strong>: Not a spaces fan? Switch to
|
||||
tabs and set your indentation size.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Vim key bindings</strong>: Rejoice Vim lovers!
|
||||
</li>
|
||||
<li>
|
||||
<strong>Code blast</strong>: Why don't you try coding with this
|
||||
switched on from the settings? Go on...
|
||||
</li>
|
||||
<li>
|
||||
<strong>Important</strong>: Due to security policy changes from
|
||||
Chrome 57 onwards, Web Maker now allows loading external
|
||||
JavaScript libraries only from certain whitelisted domains
|
||||
(localhost, https://ajax.googleapis.com, https://code.jquery.com,
|
||||
https://cdnjs.cloudflare.com, https://unpkg.com,
|
||||
https://maxcdn.com, https://cdn77.com,
|
||||
https://maxcdn.bootstrapcdn.com, https://cdn.jsdelivr.net/)
|
||||
</li>
|
||||
<li>Save button now highlights when you have unsaved changes.</li>
|
||||
<li>Jade is now called Pug. Just a name change.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.3.2</span>
|
||||
<ul>
|
||||
<li>Update Babel to support latest and coolest ES6 features.</li>
|
||||
<li>Improve onboarding experience at first install.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.3.1</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - Splitting of code and preview panes is
|
||||
remembered by the editor.
|
||||
</li>
|
||||
<li>
|
||||
Title of the creation is used for the file name when saving as
|
||||
HTML.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.3.0</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Add Library Autocompletion</strong> - Just start typing
|
||||
the name of library and you'll be shown matching libraries from
|
||||
cdnjs.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Preview Screenshot Capture</strong> - Want to grab a nice
|
||||
screenshot of your creation. You have it! Click and capture.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Auto Indent Code</strong> - Select your code and hit
|
||||
Shift-Tab to auto-indent it!
|
||||
</li>
|
||||
<li>
|
||||
<strong>Keyboard Navigation in Saved List</strong> - Now select
|
||||
your creation using arrow keys and hit ENTER to open it.
|
||||
</li>
|
||||
<li>Highlight active line in code panes.</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - Fix in generated title of new creation.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - HTML autocompletion is manual triggered
|
||||
now with Ctrl+Space.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.2.0</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Code Autocompletion</strong> - See code suggestions while
|
||||
you type!
|
||||
</li>
|
||||
<li>
|
||||
<strong>Full Screen Preview</strong> - Checkout your creation in a
|
||||
full-screen layout.
|
||||
</li>
|
||||
<li>
|
||||
<strong>SASS</strong> - SASS support added for CSS.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Faster CSS update</strong> - Preview updates instantly
|
||||
without refresh when just CSS is changed.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - Indentation fixed while going on new
|
||||
line.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - Works even in Chrome Canary now. Though
|
||||
libraries can be added only through CDNs.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<span class="notification__version">2.1.0</span>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>TypeScript</strong> - Now you can code in TypeScript too!
|
||||
</li>
|
||||
<li>
|
||||
<strong>Stylus Preprocessor</strong> - Stylus supported adding for
|
||||
CSS.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Code Folding</strong> - Collapse large code blocks for
|
||||
easy editing.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bugfix</strong> - Support JSX in JavaScript.
|
||||
</li>
|
||||
<li>Better onboarding for first time users.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<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>
|
||||
</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>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="notification">
|
||||
<span class="notification__version">1.6.0</span>
|
||||
<ul>
|
||||
<li>
|
||||
You can now configure Web-Maker to not replace new tab page from
|
||||
the settings. It is always accessible from the icon in the
|
||||
top-right.
|
||||
</li>
|
||||
<li>
|
||||
Download current code as HTML file with Ctrl/⌘ + S keyboard
|
||||
shortcut.
|
||||
</li>
|
||||
<li>
|
||||
New notifications panel added so you are always aware of the new
|
||||
changes in Web-Maker.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
314
webmaker/src/components/Settings.jsx
Normal file
314
webmaker/src/components/Settings.jsx
Normal file
@@ -0,0 +1,314 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
export default class Settings extends Component {
|
||||
updateSetting(e) {
|
||||
this.props.onChange(e);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Settings</h1>
|
||||
|
||||
<h3>Indentation</h3>
|
||||
<div
|
||||
class="line"
|
||||
title="I know this is tough, but you have to decide one!"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
checked="true"
|
||||
name="indentation"
|
||||
value="spaces"
|
||||
d-change="updateSetting"
|
||||
data-setting="indentWith"
|
||||
/>{' '}
|
||||
Spaces
|
||||
</label>
|
||||
<label class="ml-1">
|
||||
<input
|
||||
type="radio"
|
||||
name="indentation"
|
||||
value="tabs"
|
||||
d-change="updateSetting"
|
||||
data-setting="indentWith"
|
||||
/>{' '}
|
||||
Tabs
|
||||
</label>
|
||||
</div>
|
||||
<label class="line" title="">
|
||||
Indentation Size{' '}
|
||||
<input
|
||||
type="range"
|
||||
class="va-m ml-1"
|
||||
value="2"
|
||||
min="1"
|
||||
max="7"
|
||||
list="indentationSizeList"
|
||||
data-setting="indentSize"
|
||||
d-change="updateSetting"
|
||||
/>
|
||||
<span id="indentationSizeValueEl" />
|
||||
<datalist id="indentationSizeList">
|
||||
<option>1</option>
|
||||
<option>2</option>
|
||||
<option>3</option>
|
||||
<option>4</option>
|
||||
<option>5</option>
|
||||
<option>6</option>
|
||||
<option>7</option>
|
||||
</datalist>
|
||||
</label>
|
||||
|
||||
<hr />
|
||||
|
||||
<h3>Editor</h3>
|
||||
<div class="flex block--mobile">
|
||||
<div>
|
||||
<label class="line">Default Preprocessors</label>
|
||||
<div class="flex line">
|
||||
<select
|
||||
style="flex:1;margin-left:20px"
|
||||
data-setting="htmlMode"
|
||||
d-change="updateSetting"
|
||||
>
|
||||
<option value="html">HTML</option>
|
||||
<option value="markdown">Markdown</option>
|
||||
<option value="jade">Pug</option>
|
||||
</select>
|
||||
<select
|
||||
style="flex:1;margin-left:20px"
|
||||
data-setting="cssMode"
|
||||
d-change="updateSetting"
|
||||
>
|
||||
<option value="css">CSS</option>
|
||||
<option value="scss">SCSS</option>
|
||||
<option value="sass">SASS</option>
|
||||
<option value="less">LESS</option>
|
||||
<option value="stylus">Stylus</option>
|
||||
<option value="acss">Atomic CSS</option>
|
||||
</select>
|
||||
<select
|
||||
style="flex:1;margin-left:20px"
|
||||
data-setting="jsMode"
|
||||
d-change="updateSetting"
|
||||
>
|
||||
<option value="js">JS</option>
|
||||
<option value="coffee">CoffeeScript</option>
|
||||
<option value="es6">ES6 (Babel)</option>
|
||||
<option value="typescript">TypeScript</option>
|
||||
</select>
|
||||
</div>
|
||||
<label class="line">
|
||||
Theme
|
||||
<select
|
||||
style="flex:1;margin:0 20px"
|
||||
data-setting="editorTheme"
|
||||
d-change="updateSetting"
|
||||
/>
|
||||
</label>
|
||||
<label class="line">
|
||||
Font
|
||||
<select
|
||||
style="flex:1;margin:0 20px"
|
||||
data-setting="editorFont"
|
||||
d-change="updateSetting"
|
||||
>
|
||||
<option value="FiraCode">Fira Code</option>
|
||||
<option value="Inconsolata">Inconsolata</option>
|
||||
<option value="Monoid">Monoid</option>
|
||||
<option value="FixedSys">FixedSys</option>
|
||||
<option disabled="disabled">----</option>
|
||||
<option value="other">Other font from system</option>
|
||||
</select>
|
||||
<input
|
||||
id="customEditorFontInput"
|
||||
type="text"
|
||||
value=""
|
||||
placeholder="Custom font name here"
|
||||
data-setting="editorCustomFont"
|
||||
d-change="updateSetting"
|
||||
/>
|
||||
</label>
|
||||
<label class="line">
|
||||
Font Size{' '}
|
||||
<input
|
||||
style="width:70px"
|
||||
type="number"
|
||||
value={this.props.prefs.fontSize}
|
||||
data-setting="fontSize"
|
||||
onChange={this.updateSetting.bind(this)}
|
||||
/>{' '}
|
||||
px
|
||||
</label>
|
||||
<div class="line">
|
||||
Key bindings
|
||||
<label class="ml-1">
|
||||
<input
|
||||
type="radio"
|
||||
checked="true"
|
||||
name="keymap"
|
||||
value="sublime"
|
||||
d-change="updateSetting"
|
||||
data-setting="keymap"
|
||||
/>{' '}
|
||||
Sublime
|
||||
</label>
|
||||
<label class="ml-1">
|
||||
<input
|
||||
type="radio"
|
||||
name="keymap"
|
||||
value="vim"
|
||||
d-change="updateSetting"
|
||||
data-setting="keymap"
|
||||
/>{' '}
|
||||
Vim
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-2 ml-0--mobile">
|
||||
<label
|
||||
class="line"
|
||||
title="Toggle wrapping of long sentences onto new line"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
d-change="updateSetting"
|
||||
data-setting="lineWrap"
|
||||
/>{' '}
|
||||
Line wrap
|
||||
</label>
|
||||
<label
|
||||
class="line"
|
||||
title="Your Preview will refresh when you resize the preview split"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
d-change="updateSetting"
|
||||
data-setting="refreshOnResize"
|
||||
/>{' '}
|
||||
Refresh preview on resize
|
||||
</label>
|
||||
<label
|
||||
class="line"
|
||||
title="Turns on the auto-completion suggestions as you type"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
d-change="updateSetting"
|
||||
data-setting="autoComplete"
|
||||
/>{' '}
|
||||
Auto-complete suggestions
|
||||
</label>
|
||||
<label
|
||||
class="line"
|
||||
title="Refreshes the preview as you code. Otherwise use the Run button"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
d-change="updateSetting"
|
||||
data-setting="autoPreview"
|
||||
/>{' '}
|
||||
Auto-preview
|
||||
</label>
|
||||
<label
|
||||
class="line"
|
||||
title="Auto-save keeps saving your code at regular intervals after you hit the first save manually"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
d-change="updateSetting"
|
||||
data-setting="autoSave"
|
||||
/>{' '}
|
||||
Auto-save
|
||||
</label>
|
||||
<label
|
||||
class="line"
|
||||
title="Loads the last open creation when app starts"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
d-change="updateSetting"
|
||||
data-setting="preserveLastCode"
|
||||
/>{' '}
|
||||
Preserve last written code
|
||||
</label>
|
||||
<label
|
||||
class="line show-when-extension"
|
||||
title="Turning this on will start showing Web Maker in every new tab you open"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
d-change="updateSetting"
|
||||
data-setting="replaceNewTab"
|
||||
/>{' '}
|
||||
Replace new tab page
|
||||
</label>
|
||||
<label
|
||||
class="line"
|
||||
title="Preserves the console logs across your preview refreshes"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
d-change="updateSetting"
|
||||
data-setting="preserveConsoleLogs"
|
||||
/>{' '}
|
||||
Preserve console logs
|
||||
</label>
|
||||
<label
|
||||
class="line"
|
||||
title="Switch to lighter version for better performance. Removes things like blur etc."
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
d-change="updateSetting"
|
||||
data-setting="lightVersion"
|
||||
/>{' '}
|
||||
Fast/light version
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<h3>Fun</h3>
|
||||
<p>
|
||||
<label
|
||||
class="line"
|
||||
title="Enjoy wonderful particle blasts while you type"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
d-change="updateSetting"
|
||||
data-setting="isCodeBlastOn"
|
||||
/>{' '}
|
||||
Code blast!
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<h3>Advanced</h3>
|
||||
<p>
|
||||
<label
|
||||
class="line"
|
||||
title="This timeout is used to indentify a possible infinite loop and prevent it."
|
||||
>
|
||||
Maximum time allowed in a loop iteration
|
||||
<input
|
||||
type="number"
|
||||
data-setting="infiniteLoopTimeout"
|
||||
d-change="updateSetting"
|
||||
/>{' '}
|
||||
ms
|
||||
</label>
|
||||
<div class="help-text">
|
||||
If any loop iteration takes more than the defined time, it is
|
||||
detected as a potential infinite loop and further iterations are
|
||||
stopped.
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@@ -88,6 +88,7 @@ export default class UserCodeMirror extends Component {
|
||||
CodeMirror.commands.autocomplete(cm, null, { completeSingle: false });
|
||||
});
|
||||
}
|
||||
this.props.onCreation(this.cm);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@@ -9,24 +9,146 @@ import AddLibrary from './AddLibrary.jsx';
|
||||
import Modal from './Modal.jsx';
|
||||
import HelpModal from './HelpModal.jsx';
|
||||
import { log } from '../utils';
|
||||
import { itemService } from '../itemService';
|
||||
import '../db';
|
||||
import Notifications from './Notifications';
|
||||
import Settings from './Settings.jsx';
|
||||
import { modes, cssModes } from '../codeModes';
|
||||
|
||||
if (module.hot) {
|
||||
require('preact/debug');
|
||||
}
|
||||
|
||||
const LocalStorageKeys = {
|
||||
LOGIN_AND_SAVE_MESSAGE_SEEN: 'loginAndsaveMessageSeen',
|
||||
ASKED_TO_IMPORT_CREATIONS: 'askedToImportCreations'
|
||||
};
|
||||
|
||||
export default class App extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.AUTO_SAVE_INTERVAL = 15000; // 15 seconds
|
||||
|
||||
this.state = {
|
||||
isSavedItemPaneOpen: false,
|
||||
isModalOpen: false,
|
||||
isAddLibraryModalOpen: false,
|
||||
isHelpModalOpen: false,
|
||||
isNotificationsModalOpen: false,
|
||||
prefs: {},
|
||||
currentItem: {
|
||||
title: '',
|
||||
externalLibs: { js: '', css: '' }
|
||||
}
|
||||
};
|
||||
this.defaultSettings = {
|
||||
preserveLastCode: true,
|
||||
replaceNewTab: false,
|
||||
htmlMode: 'html',
|
||||
jsMode: 'js',
|
||||
cssMode: 'css',
|
||||
isCodeBlastOn: false,
|
||||
indentWith: 'spaces',
|
||||
indentSize: 2,
|
||||
editorTheme: 'monokai',
|
||||
keymap: 'sublime',
|
||||
fontSize: 16,
|
||||
refreshOnResize: false,
|
||||
autoPreview: true,
|
||||
editorFont: 'FiraCode',
|
||||
editorCustomFont: '',
|
||||
autoSave: true,
|
||||
autoComplete: true,
|
||||
preserveConsoleLogs: true,
|
||||
lightVersion: false,
|
||||
lineWrap: true,
|
||||
infiniteLoopTimeout: 1000
|
||||
};
|
||||
this.prefs = {};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
var lastCode;
|
||||
window.onunload = () => {
|
||||
this.saveCode('code');
|
||||
if (this.detachedWindow) {
|
||||
this.detachedWindow.close();
|
||||
}
|
||||
};
|
||||
|
||||
db.local.get(
|
||||
{
|
||||
layoutMode: 1,
|
||||
code: ''
|
||||
},
|
||||
result => {
|
||||
// this.toggleLayout(result.layoutMode);
|
||||
this.prefs.layoutMode = result.layoutMode;
|
||||
if (result.code) {
|
||||
lastCode = result.code;
|
||||
}
|
||||
}
|
||||
);
|
||||
// Get synced `preserveLastCode` setting to get back last code (or not).
|
||||
db.getSettings(this.defaultSettings).then(result => {
|
||||
if (result.preserveLastCode && lastCode) {
|
||||
this.setState({ unsavedEditCount: 0 });
|
||||
// For web app environment we don't fetch item from localStorage,
|
||||
// because the item isn't stored in the localStorage.
|
||||
if (lastCode.id && window.IS_EXTENSION) {
|
||||
db.local.get(lastCode.id, itemResult => {
|
||||
if (itemResult[lastCode.id]) {
|
||||
log('Load item ', lastCode.id);
|
||||
this.state.currentItem = itemResult[lastCode.id];
|
||||
this.refreshEditor();
|
||||
this.setState({ currentItem: this.state.currentItem });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log('Load last unsaved item', lastCode);
|
||||
this.state.currentItem = lastCode;
|
||||
this.refreshEditor();
|
||||
this.setState({ currentItem: this.state.currentItem });
|
||||
}
|
||||
} else {
|
||||
this.createNewItem();
|
||||
}
|
||||
Object.assign(this.state.prefs, result);
|
||||
this.setState({ prefs: this.state.prefs });
|
||||
this.updateSetting();
|
||||
});
|
||||
}
|
||||
refreshEditor() {}
|
||||
createNewItem() {
|
||||
var d = new Date();
|
||||
this.setCurrentItem({
|
||||
title:
|
||||
'Untitled ' +
|
||||
d.getDate() +
|
||||
'-' +
|
||||
(d.getMonth() + 1) +
|
||||
'-' +
|
||||
d.getHours() +
|
||||
':' +
|
||||
d.getMinutes(),
|
||||
html: '',
|
||||
css: '',
|
||||
js: '',
|
||||
externalLibs: { js: '', css: '' },
|
||||
layoutMode: this.state.currentLayoutMode
|
||||
});
|
||||
this.refreshEditor();
|
||||
// alertsService.add('New item created');
|
||||
}
|
||||
setCurrentItem(item) {
|
||||
this.state.currentItem = item;
|
||||
log('Current Item set', item);
|
||||
|
||||
// Reset auto-saving flag
|
||||
this.isAutoSavingEnabled = false;
|
||||
// Reset unsaved count, in UI also.
|
||||
this.setState({ unsavedEditCount: 0 });
|
||||
// saveBtn.classList.remove('is-marked');
|
||||
}
|
||||
openSavedItemsPane() {
|
||||
this.setState({ isSavedItemPaneOpen: true });
|
||||
@@ -78,6 +200,40 @@ export default class App extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
saveFile() {
|
||||
var htmlPromise = computeHtml();
|
||||
var cssPromise = computeCss();
|
||||
var jsPromise = computeJs(false);
|
||||
Promise.all([htmlPromise, cssPromise, jsPromise]).then(function(result) {
|
||||
var html = result[0],
|
||||
css = result[1],
|
||||
js = result[2];
|
||||
|
||||
var fileContent = getCompleteHtml(html, css, js, true);
|
||||
|
||||
var d = new Date();
|
||||
var fileName = [
|
||||
'web-maker',
|
||||
d.getFullYear(),
|
||||
d.getMonth() + 1,
|
||||
d.getDate(),
|
||||
d.getHours(),
|
||||
d.getMinutes(),
|
||||
d.getSeconds()
|
||||
].join('-');
|
||||
|
||||
if (currentItem.title) {
|
||||
fileName = currentItem.title;
|
||||
}
|
||||
fileName += '.html';
|
||||
|
||||
var blob = new Blob([fileContent], { type: 'text/html;charset=UTF-8' });
|
||||
utils.downloadFile(fileName, blob);
|
||||
|
||||
// trackEvent('fn', 'saveFileComplete');
|
||||
});
|
||||
}
|
||||
|
||||
closeAllOverlays() {
|
||||
if (this.state.isSavedItemPaneOpen) {
|
||||
this.setState({ isSavedItemPaneOpen: false });
|
||||
@@ -136,6 +292,211 @@ export default class App extends Component {
|
||||
// trackEvent('ui', 'toggleLayoutClick', mode);
|
||||
this.toggleLayout(layoutId);
|
||||
}
|
||||
saveSetting(setting, value) {
|
||||
const d = deferred();
|
||||
const obj = {
|
||||
[setting]: value
|
||||
};
|
||||
db.local.set(obj, d.resolve);
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
saveCode(key) {
|
||||
// this.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;
|
||||
if (modes['css' || cssMode].hasSettings) {
|
||||
this.state.currentItem.cssSettings = {
|
||||
acssConfig: scope.acssSettingsCm.getValue()
|
||||
};
|
||||
}
|
||||
this.state.currentItem.updatedOn = Date.now();
|
||||
this.state.currentItem.layoutMode = this.state.currentLayoutMode;
|
||||
// this.state.currentItem.externalLibs = {
|
||||
// js: externalJsTextarea.value,
|
||||
// css: externalCssTextarea.value
|
||||
// };
|
||||
|
||||
// currentItem.sizes = getCodePaneSizes();
|
||||
// currentItem.mainSizes = getMainPaneSizes();
|
||||
|
||||
log('saving key', key || this.state.currentItem.id, this.state.currentItem);
|
||||
|
||||
function onSaveComplete() {
|
||||
if (window.user && !navigator.onLine) {
|
||||
// alertsService.add(
|
||||
// 'Item saved locally. Will save to account when you are online.'
|
||||
// );
|
||||
} else {
|
||||
// alertsService.add('Item saved.');
|
||||
}
|
||||
this.state.unsavedEditCount = 0;
|
||||
// saveBtn.classList.remove('is-marked');
|
||||
}
|
||||
|
||||
return itemService
|
||||
.setItem(key || this.state.currentItem.id, this.state.currentItem)
|
||||
.then(onSaveComplete);
|
||||
}
|
||||
|
||||
// Save current item to storage
|
||||
saveItem() {
|
||||
if (
|
||||
!window.user &&
|
||||
!window.localStorage[LocalStorageKeys.LOGIN_AND_SAVE_MESSAGE_SEEN]
|
||||
) {
|
||||
const answer = confirm(
|
||||
'Saving without signing in will save your work only on this machine and this browser. If you want it to be secure & available anywhere, please login in your account and then save.\n\nDo you still want to continue saving locally?'
|
||||
);
|
||||
window.localStorage[LocalStorageKeys.LOGIN_AND_SAVE_MESSAGE_SEEN] = true;
|
||||
if (!answer) {
|
||||
trackEvent('ui', LocalStorageKeys.LOGIN_AND_SAVE_MESSAGE_SEEN, 'login');
|
||||
closeAllOverlays();
|
||||
loginModal.classList.add('is-modal-visible');
|
||||
return;
|
||||
}
|
||||
trackEvent('ui', LocalStorageKeys.LOGIN_AND_SAVE_MESSAGE_SEEN, 'local');
|
||||
}
|
||||
var isNewItem = !currentItem.id;
|
||||
currentItem.id = currentItem.id || 'item-' + utils.generateRandomId();
|
||||
saveBtn.classList.add('is-loading');
|
||||
this.saveCode().then(() => {
|
||||
saveBtn.classList.remove('is-loading');
|
||||
// If this is the first save, and auto-saving settings is enabled,
|
||||
// then start auto-saving from now on.
|
||||
// This is done in `saveCode()` completion so that the
|
||||
// auto-save notification overrides the `saveCode` function's notification.
|
||||
if (!isAutoSavingEnabled && prefs.autoSave) {
|
||||
isAutoSavingEnabled = true;
|
||||
alertsService.add('Auto-save enabled.');
|
||||
}
|
||||
});
|
||||
// Push into the items hash if its a new item being saved
|
||||
if (isNewItem) {
|
||||
itemService.setItemForUser(currentItem.id);
|
||||
}
|
||||
}
|
||||
onCodeChange(type, code) {
|
||||
this.state.currentItem[type] = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles all user triggered preference changes in the UI.
|
||||
*/
|
||||
updateSetting(e) {
|
||||
// If this was triggered from user interaction, save the setting
|
||||
if (e) {
|
||||
var settingName = e.target.dataset.setting;
|
||||
var obj = {};
|
||||
var el = e.target;
|
||||
log(settingName, el.type === 'checkbox' ? el.checked : el.value);
|
||||
this.state.prefs[settingName] =
|
||||
el.type === 'checkbox' ? el.checked : el.value;
|
||||
obj[settingName] = this.state.prefs[settingName];
|
||||
|
||||
// We always save locally so that it gets fetched
|
||||
// faster on future loads.
|
||||
db.sync.set(obj, function() {
|
||||
// alertsService.add('Setting saved');
|
||||
});
|
||||
if (window.user) {
|
||||
window.db.getDb().then(remoteDb => {
|
||||
remoteDb
|
||||
.collection('users')
|
||||
.doc(window.user.uid)
|
||||
.update({
|
||||
[`settings.${settingName}`]: this.state.prefs[settingName]
|
||||
})
|
||||
.then(arg => {
|
||||
utils.log(`Setting "${settingName}" for user`, arg);
|
||||
})
|
||||
.catch(error => utils.log(error));
|
||||
});
|
||||
}
|
||||
// trackEvent('ui', 'updatePref-' + settingName, prefs[settingName]);
|
||||
}
|
||||
|
||||
const prefs = this.state.prefs;
|
||||
// Show/hide RUN button based on autoPreview setting.
|
||||
runBtn.classList[prefs.autoPreview ? 'add' : 'remove']('hide');
|
||||
|
||||
// htmlCode.querySelector('.CodeMirror').style.fontSize = prefs.fontSize;
|
||||
// cssCode.querySelector('.CodeMirror').style.fontSize = prefs.fontSize;
|
||||
// jsCode.querySelector('.CodeMirror').style.fontSize = prefs.fontSize;
|
||||
// consoleEl.querySelector('.CodeMirror').style.fontSize = prefs.fontSize;
|
||||
|
||||
// Update indentation count when slider is updated
|
||||
// indentationSizeValueEl.textContent = $('[data-setting=indentSize]').value;
|
||||
|
||||
// Replace correct css file in LINK tags's href
|
||||
// editorThemeLinkTag.href = `lib/codemirror/theme/${prefs.editorTheme}.css`;
|
||||
// fontStyleTag.textContent = fontStyleTemplate.textContent.replace(
|
||||
// /fontname/g,
|
||||
// (prefs.editorFont === 'other'
|
||||
// ? prefs.editorCustomFont
|
||||
// : prefs.editorFont) || 'FiraCode'
|
||||
// );
|
||||
// customEditorFontInput.classList[
|
||||
// prefs.editorFont === 'other' ? 'remove' : 'add'
|
||||
// ]('hide');
|
||||
|
||||
/* ['html', 'js', 'css'].forEach(type => {
|
||||
scope.cm[type].setOption(
|
||||
'indentWithTabs',
|
||||
$('[data-setting=indentWith]:checked').value !== 'spaces'
|
||||
);
|
||||
scope.cm[type].setOption(
|
||||
'blastCode',
|
||||
$('[data-setting=isCodeBlastOn]').checked
|
||||
? { effect: 2, shake: false }
|
||||
: false
|
||||
);
|
||||
scope.cm[type].setOption(
|
||||
'indentUnit',
|
||||
+$('[data-setting=indentSize]').value
|
||||
);
|
||||
scope.cm[type].setOption(
|
||||
'tabSize',
|
||||
+$('[data-setting=indentSize]').value
|
||||
);
|
||||
scope.cm[type].setOption('theme', $('[data-setting=editorTheme]').value);
|
||||
|
||||
scope.cm[type].setOption(
|
||||
'keyMap',
|
||||
$('[data-setting=keymap]:checked').value
|
||||
);
|
||||
scope.cm[type].setOption(
|
||||
'lineWrapping',
|
||||
$('[data-setting=lineWrap]').checked
|
||||
);
|
||||
scope.cm[type].refresh();
|
||||
});
|
||||
scope.consoleCm.setOption('theme', $('[data-setting=editorTheme]').value);
|
||||
scope.acssSettingsCm.setOption(
|
||||
'theme',
|
||||
$('[data-setting=editorTheme]').value
|
||||
); */
|
||||
if (prefs.autoSave) {
|
||||
if (!this.autoSaveInterval) {
|
||||
this.autoSaveInterval = setInterval(
|
||||
this.autoSaveLoop.bind(this),
|
||||
this.AUTO_SAVE_INTERVAL
|
||||
);
|
||||
}
|
||||
} else {
|
||||
clearInterval(this.autoSaveInterval);
|
||||
this.autoSaveInterval = null;
|
||||
}
|
||||
|
||||
document.body.classList[prefs.lightVersion ? 'add' : 'remove'](
|
||||
'light-version'
|
||||
);
|
||||
}
|
||||
autoSaveLoop() {}
|
||||
|
||||
render() {
|
||||
return (
|
||||
@@ -146,11 +507,20 @@ export default class App extends Component {
|
||||
openBtnHandler={this.openSavedItemsPane.bind(this)}
|
||||
addLibraryBtnHandler={this.openAddLibrary.bind(this)}
|
||||
/>
|
||||
<ContentWrap currentItem={this.state.currentItem} />
|
||||
<ContentWrap
|
||||
currentItem={this.state.currentItem}
|
||||
onCodeChange={this.onCodeChange.bind(this)}
|
||||
/>
|
||||
<div class="global-console-container" id="globalConsoleContainerEl" />
|
||||
<Footer
|
||||
layoutBtnClickHandler={this.layoutBtnClickHandler.bind(this)}
|
||||
helpBtnClickHandler={() => this.setState({ isHelpModalOpen: true })}
|
||||
settingsBtnClickHandler={() =>
|
||||
this.setState({ isSettingsModalOpen: true })
|
||||
}
|
||||
notificationsBtnClickHandler={() =>
|
||||
this.setState({ isNotificationsModalOpen: true })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -191,6 +561,23 @@ export default class App extends Component {
|
||||
onChange={this.onExternalLibChange.bind(this)}
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
show={this.state.isNotificationsModalOpen}
|
||||
closeHandler={() =>
|
||||
this.setState({ isNotificationsModalOpen: false })
|
||||
}
|
||||
>
|
||||
<Notifications />
|
||||
</Modal>
|
||||
<Modal
|
||||
show={this.state.isSettingsModalOpen}
|
||||
closeHandler={() => this.setState({ isSettingsModalOpen: false })}
|
||||
>
|
||||
<Settings
|
||||
prefs={this.state.prefs}
|
||||
onChange={this.updateSetting.bind(this)}
|
||||
/>
|
||||
</Modal>
|
||||
<HelpModal
|
||||
show={this.state.isHelpModalOpen}
|
||||
closeHandler={() => this.setState({ isHelpModalOpen: false })}
|
||||
|
151
webmaker/src/db.js
Normal file
151
webmaker/src/db.js
Normal file
@@ -0,0 +1,151 @@
|
||||
import {
|
||||
deferred
|
||||
} from './deferred'
|
||||
(() => {
|
||||
const FAUX_DELAY = 1;
|
||||
|
||||
var db;
|
||||
var dbPromise;
|
||||
|
||||
var local = {
|
||||
get: (obj, cb) => {
|
||||
const retVal = {};
|
||||
if (typeof obj === 'string') {
|
||||
retVal[obj] = JSON.parse(window.localStorage.getItem(obj));
|
||||
setTimeout(() => cb(retVal), FAUX_DELAY);
|
||||
} else {
|
||||
Object.keys(obj).forEach(key => {
|
||||
const val = window.localStorage.getItem(key);
|
||||
retVal[key] =
|
||||
val === undefined || val === null ? obj[key] : JSON.parse(val);
|
||||
});
|
||||
setTimeout(() => cb(retVal), FAUX_DELAY);
|
||||
}
|
||||
},
|
||||
set: (obj, cb) => {
|
||||
Object.keys(obj).forEach(key => {
|
||||
window.localStorage.setItem(key, JSON.stringify(obj[key]));
|
||||
});
|
||||
/* eslint-disable consistent-return */
|
||||
setTimeout(() => {
|
||||
if (cb) {
|
||||
return cb();
|
||||
}
|
||||
}, FAUX_DELAY);
|
||||
/* eslint-enable consistent-return */
|
||||
}
|
||||
};
|
||||
const dbLocalAlias = chrome && chrome.storage ? chrome.storage.local : local;
|
||||
const dbSyncAlias = chrome && chrome.storage ? chrome.storage.sync : local;
|
||||
|
||||
async function getDb() {
|
||||
if (dbPromise) {
|
||||
return dbPromise;
|
||||
}
|
||||
utils.log('Initializing firestore');
|
||||
dbPromise = new Promise((resolve, reject) => {
|
||||
if (db) {
|
||||
return resolve(db);
|
||||
}
|
||||
return firebase
|
||||
.firestore()
|
||||
.enablePersistence()
|
||||
.then(function () {
|
||||
// Initialize Cloud Firestore through firebase
|
||||
db = firebase.firestore();
|
||||
utils.log('firebase db ready', db);
|
||||
resolve(db);
|
||||
})
|
||||
.catch(function (err) {
|
||||
reject(err.code);
|
||||
if (err.code === 'failed-precondition') {
|
||||
// Multiple tabs open, persistence can only be enabled
|
||||
// in one tab at a a time.
|
||||
alert(
|
||||
"Opening Web Maker web app in multiple tabs isn't supported at present and it seems like you already have it opened in another tab. Please use in one tab."
|
||||
);
|
||||
window.trackEvent('fn', 'multiTabError');
|
||||
} else if (err.code === 'unimplemented') {
|
||||
// The current browser does not support all of the
|
||||
// features required to enable persistence
|
||||
// ...
|
||||
}
|
||||
});
|
||||
});
|
||||
return dbPromise;
|
||||
}
|
||||
|
||||
async function getUserLastSeenVersion() {
|
||||
const d = deferred();
|
||||
// Will be chrome.storage.sync in extension environment,
|
||||
// otherwise will fallback to localstorage
|
||||
dbSyncAlias.get({
|
||||
lastSeenVersion: ''
|
||||
},
|
||||
result => {
|
||||
d.resolve(result.lastSeenVersion);
|
||||
}
|
||||
);
|
||||
return d.promise;
|
||||
// Might consider getting actual value from remote db.
|
||||
// Not critical right now.
|
||||
}
|
||||
|
||||
async function setUserLastSeenVersion(version) {
|
||||
// Setting the `lastSeenVersion` in localStorage(sync for extension) always
|
||||
// because next time we need to fetch it irrespective of the user being
|
||||
// logged in or out quickly from local storage.
|
||||
dbSyncAlias.set({
|
||||
lastSeenVersion: version
|
||||
},
|
||||
function () {}
|
||||
);
|
||||
if (window.user) {
|
||||
const remoteDb = await getDb();
|
||||
remoteDb
|
||||
.doc(`users/${window.user.uid}`)
|
||||
.update({
|
||||
lastSeenVersion: version
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function getUser(userId) {
|
||||
const remoteDb = await getDb();
|
||||
return remoteDb
|
||||
.doc(`users/${userId}`)
|
||||
.get()
|
||||
.then(doc => {
|
||||
if (!doc.exists)
|
||||
return remoteDb.doc(`users/${userId}`).set({}, {
|
||||
merge: true
|
||||
});
|
||||
const user = doc.data();
|
||||
Object.assign(window.user, user);
|
||||
return user;
|
||||
});
|
||||
}
|
||||
|
||||
// Fetch user settings.
|
||||
// This isn't hitting the remote db because remote settings
|
||||
// get fetch asynchronously (in user/) and update the envioronment.
|
||||
function getSettings(defaultSettings) {
|
||||
const d = deferred();
|
||||
// Will be chrome.storage.sync in extension environment,
|
||||
// otherwise will fallback to localstorage
|
||||
dbSyncAlias.get(defaultSettings, result => {
|
||||
d.resolve(result);
|
||||
});
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
window.db = {
|
||||
getDb,
|
||||
getUser,
|
||||
getUserLastSeenVersion,
|
||||
setUserLastSeenVersion,
|
||||
getSettings,
|
||||
local: dbLocalAlias,
|
||||
sync: dbSyncAlias
|
||||
};
|
||||
})();
|
233
webmaker/src/itemService.js
Normal file
233
webmaker/src/itemService.js
Normal file
@@ -0,0 +1,233 @@
|
||||
import {
|
||||
deferred
|
||||
} from './deferred';
|
||||
|
||||
export const itemService = {
|
||||
|
||||
async getItem(id) {
|
||||
var remoteDb = await window.db.getDb();
|
||||
return remoteDb
|
||||
.doc(`items/${id}`)
|
||||
.get()
|
||||
.then(doc => {
|
||||
return doc.data();
|
||||
});
|
||||
},
|
||||
async getUserItemIds() {
|
||||
if (window.user) {
|
||||
return new Promise(resolve => {
|
||||
resolve(window.user.items || {});
|
||||
});
|
||||
}
|
||||
var remoteDb = await window.db.getDb();
|
||||
return remoteDb
|
||||
.doc(`users/${window.user.uid}`)
|
||||
.get()
|
||||
.then(doc => {
|
||||
if (!doc.exists) {
|
||||
return {};
|
||||
}
|
||||
return doc.data().items;
|
||||
});
|
||||
},
|
||||
|
||||
async getAllItems() {
|
||||
var t = Date.now()
|
||||
var d = deferred();
|
||||
let itemIds = await this.getUserItemIds();
|
||||
itemIds = Object.getOwnPropertyNames(itemIds || {});
|
||||
utils.log('itemids', itemIds);
|
||||
|
||||
if (!itemIds.length) {
|
||||
d.resolve([]);
|
||||
}
|
||||
var remoteDb = await window.db.getDb();
|
||||
const items = [];
|
||||
remoteDb
|
||||
.collection('items')
|
||||
.where('createdBy', '==', window.user.uid)
|
||||
.onSnapshot(function (querySnapshot) {
|
||||
querySnapshot.forEach(function (doc) {
|
||||
items.push(doc.data());
|
||||
});
|
||||
utils.log('Items fetched in ', Date.now() - t, 'ms')
|
||||
|
||||
d.resolve(items);
|
||||
}, function () {
|
||||
d.resolve([])
|
||||
});
|
||||
return d.promise;
|
||||
},
|
||||
|
||||
async setUser() {
|
||||
const remoteDb = await window.db.getDb();
|
||||
return remoteDb.doc(`users/${window.user.uid}`).set({
|
||||
items: {}
|
||||
});
|
||||
},
|
||||
|
||||
async setItem(id, item) {
|
||||
const d = deferred();
|
||||
var remotePromise;
|
||||
// TODO: check why we need to save locally always?
|
||||
const obj = {
|
||||
[id]: item
|
||||
};
|
||||
db.local.set(obj, () => {
|
||||
// Is extension OR is app but logged out OR is logged in but offline
|
||||
// If logged in but offline, resolve immediately so
|
||||
// that you see the feedback msg immediately and not wait for
|
||||
// later sync.
|
||||
if (window.IS_EXTENSION || !window.user || !navigator.onLine) {
|
||||
d.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
// If `id` is `code`, this is a call on unloadbefore to save the last open thing.
|
||||
// Do not presist that on remote.
|
||||
if (id === 'code') {
|
||||
// No deferred required here as this gets called on unloadbefore
|
||||
return false;
|
||||
}
|
||||
if (window.user) {
|
||||
var remoteDb = await window.db.getDb();
|
||||
utils.log(`Starting to save item ${id}`);
|
||||
item.createdBy = window.user.uid;
|
||||
remotePromise = remoteDb
|
||||
.collection('items')
|
||||
.doc(id)
|
||||
.set(item, {
|
||||
merge: true
|
||||
})
|
||||
.then(arg => {
|
||||
utils.log('Document written', arg);
|
||||
d.resolve();
|
||||
})
|
||||
.catch(d.reject);
|
||||
}
|
||||
|
||||
return window.user && navigator.onLine ? remotePromise : d.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Saves the passed items in the database.
|
||||
* @param {Array} items to be saved in DB
|
||||
*/
|
||||
saveItems(items) {
|
||||
var d = deferred();
|
||||
// When not logged in
|
||||
if (!window.user) {
|
||||
// save new items
|
||||
window.db.local.set(items, d.resolve);
|
||||
// Push in new item IDs
|
||||
window.db.local.get({
|
||||
items: {}
|
||||
},
|
||||
function (result) {
|
||||
/* eslint-disable guard-for-in */
|
||||
for (var id in items) {
|
||||
result.items[id] = true;
|
||||
}
|
||||
window.db.local.set({
|
||||
items: result.items
|
||||
});
|
||||
/* eslint-enable guard-for-in */
|
||||
}
|
||||
);
|
||||
} else {
|
||||
window.db.getDb().then(remoteDb => {
|
||||
const batch = remoteDb.batch();
|
||||
/* eslint-disable guard-for-in */
|
||||
for (var id in items) {
|
||||
items[id].createdBy = window.user.uid;
|
||||
batch.set(remoteDb.doc(`items/${id}`), items[id]);
|
||||
batch.update(remoteDb.doc(`users/${window.user.uid}`), {
|
||||
[`items.${id}`]: true
|
||||
});
|
||||
// Set these items on our cached user object too
|
||||
window.user.items = window.user.items || {};
|
||||
window.user.items[id] = true;
|
||||
}
|
||||
batch.commit().then(d.resolve);
|
||||
/* eslint-enable guard-for-in */
|
||||
});
|
||||
}
|
||||
return d.promise;
|
||||
},
|
||||
|
||||
async removeItem(id) {
|
||||
// When not logged in
|
||||
if (!window.user) {
|
||||
var d = deferred();
|
||||
db.local.remove(id, d.resolve);
|
||||
return d.promise;
|
||||
}
|
||||
const remoteDb = await window.db.getDb();
|
||||
utils.log(`Starting to save item ${id}`);
|
||||
return remoteDb
|
||||
.collection('items')
|
||||
.doc(id)
|
||||
.delete()
|
||||
.then(arg => {
|
||||
utils.log('Document removed', arg);
|
||||
})
|
||||
.catch(error => utils.log(error));
|
||||
},
|
||||
|
||||
async setItemForUser(itemId) {
|
||||
// When not logged in
|
||||
if (!window.user) {
|
||||
return window.db.local.get({
|
||||
items: {}
|
||||
},
|
||||
function (result) {
|
||||
result.items[itemId] = true;
|
||||
window.db.local.set({
|
||||
items: result.items
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
const remoteDb = await window.db.getDb();
|
||||
return remoteDb
|
||||
.collection('users')
|
||||
.doc(window.user.uid)
|
||||
.update({
|
||||
[`items.${itemId}`]: true
|
||||
})
|
||||
.then(arg => {
|
||||
utils.log(`Item ${itemId} set for user`, arg);
|
||||
window.user.items = window.user.items || {};
|
||||
window.user.items[itemId] = true;
|
||||
})
|
||||
.catch(error => utils.log(error));
|
||||
},
|
||||
|
||||
async unsetItemForUser(itemId) {
|
||||
// When not logged in
|
||||
if (!window.user) {
|
||||
return window.db.local.get({
|
||||
items: {}
|
||||
},
|
||||
function (result) {
|
||||
delete result.items[itemId];
|
||||
db.local.set({
|
||||
items: result.items
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
const remoteDb = await window.db.getDb();
|
||||
return remoteDb
|
||||
.collection('users')
|
||||
.doc(window.user.uid)
|
||||
.update({
|
||||
[`items.${itemId}`]: firebase.firestore.FieldValue.delete()
|
||||
})
|
||||
.then(arg => {
|
||||
delete window.user.items[itemId];
|
||||
utils.log(`Item ${itemId} unset for user`, arg);
|
||||
})
|
||||
.catch(error => utils.log(error));
|
||||
}
|
||||
}
|
153
webmaker/src/libraryList.js
Normal file
153
webmaker/src/libraryList.js
Normal file
@@ -0,0 +1,153 @@
|
||||
export const jsLibs = [
|
||||
{
|
||||
url: 'https://code.jquery.com/jquery-3.2.1.min.js',
|
||||
label: 'jQuery',
|
||||
type: 'js'
|
||||
},
|
||||
{
|
||||
url: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js',
|
||||
label: 'Bootstrap 3',
|
||||
type: 'js'
|
||||
},
|
||||
{
|
||||
url:
|
||||
'https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js',
|
||||
label: 'Bootstrap 4',
|
||||
type: 'js'
|
||||
},
|
||||
{
|
||||
url:
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/js/foundation.min.js',
|
||||
label: 'Foundation',
|
||||
type: 'js'
|
||||
},
|
||||
{
|
||||
url: 'https://semantic-ui.com/dist/semantic.min.js',
|
||||
label: 'Semantic UI',
|
||||
type: 'js'
|
||||
},
|
||||
{
|
||||
url: 'https://ajax.googleapis.com/ajax/libs/angularjs/1.6.5/angular.min.js',
|
||||
label: 'Angular',
|
||||
type: 'js'
|
||||
},
|
||||
{
|
||||
url:
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.production.min.js',
|
||||
label: 'React',
|
||||
type: 'js'
|
||||
},
|
||||
{
|
||||
url:
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.production.min.js',
|
||||
label: 'React DOM',
|
||||
type: 'js'
|
||||
},
|
||||
{
|
||||
url: 'https://unpkg.com/vue/dist/vue.min.js',
|
||||
label: 'Vue.js',
|
||||
type: 'js'
|
||||
},
|
||||
{
|
||||
url: 'https://cdnjs.cloudflare.com/ajax/libs/three.js/89/three.min.js',
|
||||
label: 'Three.js',
|
||||
type: 'js'
|
||||
},
|
||||
{
|
||||
url: 'https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/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'
|
||||
},
|
||||
{
|
||||
url: 'https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js',
|
||||
label: 'Greensock TweenMax',
|
||||
type: 'js'
|
||||
},
|
||||
{
|
||||
url: 'https://cdnjs.cloudflare.com/ajax/libs/uikit/2.27.5/js/uikit.min.js',
|
||||
label: 'UIkit 2',
|
||||
type: 'js'
|
||||
},
|
||||
{
|
||||
url:
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.42/js/uikit.min.js',
|
||||
label: 'UIkit 3',
|
||||
type: 'js'
|
||||
}
|
||||
];
|
||||
export const cssLibs = [
|
||||
{
|
||||
url:
|
||||
'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css',
|
||||
label: 'Bootstrap 3',
|
||||
type: 'css'
|
||||
},
|
||||
{
|
||||
url:
|
||||
'https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css',
|
||||
label: 'Bootstrap 4',
|
||||
type: 'css'
|
||||
},
|
||||
{
|
||||
url:
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/css/foundation.min.css',
|
||||
label: 'Foundation',
|
||||
type: 'css'
|
||||
},
|
||||
{
|
||||
url: 'https://semantic-ui.com/dist/semantic.min.css',
|
||||
label: 'Semantic UI',
|
||||
type: 'css'
|
||||
},
|
||||
{
|
||||
url: 'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css',
|
||||
label: 'Bulma',
|
||||
type: 'css'
|
||||
},
|
||||
|
||||
{
|
||||
url: 'https://cdnjs.cloudflare.com/ajax/libs/hint.css/2.5.0/hint.min.css',
|
||||
label: 'Hint.css',
|
||||
type: 'css'
|
||||
},
|
||||
{
|
||||
url: 'https://cdn.jsdelivr.net/npm/tailwindcss/dist/tailwind.min.css',
|
||||
label: 'Tailwind.css',
|
||||
type: 'css'
|
||||
},
|
||||
{
|
||||
url:
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/uikit/2.27.5/css/uikit.min.css',
|
||||
label: 'UIkit 2',
|
||||
type: 'css'
|
||||
},
|
||||
{
|
||||
url:
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.42/css/uikit.min.css',
|
||||
label: 'UIkit 3',
|
||||
type: 'css'
|
||||
},
|
||||
{
|
||||
url:
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css',
|
||||
label: 'Animate.css',
|
||||
type: 'css'
|
||||
},
|
||||
{
|
||||
url:
|
||||
'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css',
|
||||
label: 'FontAwesome 4',
|
||||
type: 'css'
|
||||
},
|
||||
{
|
||||
url: 'https://use.fontawesome.com/releases/v5.0.10/css/all.css',
|
||||
label: 'FontAwesome 5',
|
||||
type: 'css'
|
||||
}
|
||||
];
|
239
webmaker/src/utils.js
Normal file
239
webmaker/src/utils.js
Normal file
@@ -0,0 +1,239 @@
|
||||
window.DEBUG = document.cookie.indexOf('wmdebug') > -1;
|
||||
|
||||
window.$ = document.querySelector.bind(document);
|
||||
window.$all = selector => [...document.querySelectorAll(selector)];
|
||||
var alphaNum =
|
||||
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
|
||||
/**
|
||||
* The following 2 functions are supposed to find the next/previous sibling until the
|
||||
* passed `selector` is matched. But for now it actually finds the next/previous
|
||||
* element of `this` element in the list of `selector` matched element inside `this`'s
|
||||
* parent.
|
||||
* @param Selector that should match for next siblings
|
||||
* @return element Next element that mathes `selector`
|
||||
*/
|
||||
Node.prototype.nextUntil = function (selector) {
|
||||
const siblings = [...this.parentNode.querySelectorAll(selector)];
|
||||
const index = siblings.indexOf(this);
|
||||
return siblings[index + 1];
|
||||
};
|
||||
|
||||
// Safari doesn't have this!
|
||||
window.requestIdleCallback =
|
||||
window.requestIdleCallback ||
|
||||
function (fn) {
|
||||
setTimeout(fn, 10);
|
||||
};
|
||||
|
||||
/*
|
||||
* @param Selector that should match for next siblings
|
||||
* @return element Next element that mathes `selector`
|
||||
*/
|
||||
Node.prototype.previousUntil = function (selector) {
|
||||
const siblings = [...this.parentNode.querySelectorAll(selector)];
|
||||
const index = siblings.indexOf(this);
|
||||
return siblings[index - 1];
|
||||
};
|
||||
|
||||
// https://github.com/substack/semver-compare/blob/master/index.js
|
||||
export 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;
|
||||
}
|
||||
|
||||
export function generateRandomId(len) {
|
||||
var length = len || 10;
|
||||
var id = '';
|
||||
for (var i = length; i--;) {
|
||||
id += alphaNum[~~(Math.random() * alphaNum.length)];
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
export function onButtonClick(btn, listener) {
|
||||
btn.addEventListener('click', function buttonClickListener(e) {
|
||||
listener(e);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
export function log() {
|
||||
if (window.DEBUG) {
|
||||
console.log(...arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds timed limit on the loops found in the passed code.
|
||||
* Contributed by Ariya Hidayat!
|
||||
* @param code {string} Code to be protected from infinite loops.
|
||||
*/
|
||||
export function addInfiniteLoopProtection(code, {
|
||||
timeout
|
||||
}) {
|
||||
var loopId = 1;
|
||||
var patches = [];
|
||||
var varPrefix = '_wmloopvar';
|
||||
var varStr = 'var %d = Date.now();\n';
|
||||
var checkStr = `\nif (Date.now() - %d > ${timeout}) { window.top.previewException(new Error("Infinite loop")); break;}\n`;
|
||||
|
||||
esprima.parse(code, {
|
||||
tolerant: true,
|
||||
range: true,
|
||||
jsx: true
|
||||
}, function (
|
||||
node
|
||||
) {
|
||||
switch (node.type) {
|
||||
case 'DoWhileStatement':
|
||||
case 'ForStatement':
|
||||
case 'ForInStatement':
|
||||
case 'ForOfStatement':
|
||||
case 'WhileStatement':
|
||||
var start = 1 + node.body.range[0];
|
||||
var end = node.body.range[1];
|
||||
var prolog = checkStr.replace('%d', varPrefix + loopId);
|
||||
var epilog = '';
|
||||
|
||||
if (node.body.type !== 'BlockStatement') {
|
||||
// `while(1) doThat()` becomes `while(1) {doThat()}`
|
||||
prolog = '{' + prolog;
|
||||
epilog = '}';
|
||||
--start;
|
||||
}
|
||||
|
||||
patches.push({
|
||||
pos: start,
|
||||
str: prolog
|
||||
});
|
||||
patches.push({
|
||||
pos: end,
|
||||
str: epilog
|
||||
});
|
||||
patches.push({
|
||||
pos: node.range[0],
|
||||
str: varStr.replace('%d', varPrefix + loopId)
|
||||
});
|
||||
++loopId;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
/* eslint-disable no-param-reassign */
|
||||
patches
|
||||
.sort(function (a, b) {
|
||||
return b.pos - a.pos;
|
||||
})
|
||||
.forEach(function (patch) {
|
||||
code = code.slice(0, patch.pos) + patch.str + code.slice(patch.pos);
|
||||
});
|
||||
|
||||
/* eslint-disable no-param-reassign */
|
||||
return code;
|
||||
}
|
||||
|
||||
export 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;
|
||||
}
|
||||
|
||||
// create a one-time event
|
||||
export function once(node, type, callback) {
|
||||
// create event
|
||||
node.addEventListener(type, function (e) {
|
||||
// remove event
|
||||
e.target.removeEventListener(type, arguments.callee);
|
||||
// call handler
|
||||
return callback(e);
|
||||
});
|
||||
}
|
||||
|
||||
export function downloadFile(fileName, blob) {
|
||||
function downloadWithAnchor() {
|
||||
var a = document.createElement('a');
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.download = fileName;
|
||||
a.style.display = 'none';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
}
|
||||
if (window.IS_EXTENSION) {
|
||||
chrome.downloads.download({
|
||||
url: window.URL.createObjectURL(blob),
|
||||
filename: fileName,
|
||||
saveAs: true
|
||||
},
|
||||
() => {
|
||||
// If there was an error, just download the file using ANCHOR method.
|
||||
if (chrome.runtime.lastError) {
|
||||
downloadWithAnchor();
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
downloadWithAnchor();
|
||||
}
|
||||
}
|
||||
|
||||
window.utils = {
|
||||
semverCompare,
|
||||
generateRandomId,
|
||||
onButtonClick,
|
||||
addInfiniteLoopProtection,
|
||||
getHumanDate,
|
||||
log,
|
||||
once,
|
||||
downloadFile
|
||||
};
|
||||
|
||||
window.chrome = window.chrome || {};
|
||||
window.chrome.i18n = {
|
||||
getMessage: () => {}
|
||||
};
|
||||
|
||||
window.IS_EXTENSION = !!window.chrome.extension;
|
||||
if (window.IS_EXTENSION) {
|
||||
document.body.classList.add('is-extension');
|
||||
} else {
|
||||
document.body.classList.add('is-app');
|
||||
}
|
Reference in New Issue
Block a user