1
0
mirror of https://github.com/chinchang/web-maker.git synced 2025-07-27 16:50:11 +02:00

code mode functionality added

This commit is contained in:
Kushagra Gour
2018-06-03 17:22:43 +05:30
parent a5ca35ac90
commit d407304ebf
7 changed files with 258 additions and 40 deletions

View File

@@ -0,0 +1,60 @@
// Most of the code from this file comes from:
// https://github.com/codemirror/CodeMirror/blob/master/addon/mode/loadmode.js
import CodeMirror from 'codemirror';
// Make CodeMirror available globally so the modes' can register themselves.
window.CodeMirror = CodeMirror
if (!CodeMirror.modeURL) CodeMirror.modeURL = 'lib/codemirror/mode/%N/%N.js';
var loading = {}
function splitCallback(cont, n) {
var countDown = n
return function () {
if (--countDown === 0) cont()
}
}
function ensureDeps(mode, cont) {
var deps = CodeMirror.modes[mode].dependencies
if (!deps) return cont()
var missing = []
for (var i = 0; i < deps.length; ++i) {
if (!CodeMirror.modes.hasOwnProperty(deps[i])) missing.push(deps[i])
}
if (!missing.length) return cont()
var split = splitCallback(cont, missing.length)
for (i = 0; i < missing.length; ++i) CodeMirror.requireMode(missing[i], split)
}
CodeMirror.requireMode = function (mode, cont) {
if (typeof mode !== 'string') mode = mode.name
if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont)
if (loading.hasOwnProperty(mode)) return loading[mode].push(cont)
var file = CodeMirror.modeURL.replace(/%N/g, mode)
var script = document.createElement('script')
script.src = file
var others = document.getElementsByTagName('script')[0]
var list = loading[mode] = [cont]
CodeMirror.on(script, 'load', function () {
ensureDeps(mode, function () {
for (var i = 0; i < list.length; ++i) list[i]()
})
})
others.parentNode.insertBefore(script, others)
}
CodeMirror.autoLoadMode = function (instance, mode) {
if (CodeMirror.modes.hasOwnProperty(mode)) return
CodeMirror.requireMode(mode, function () {
instance.setOption('mode', instance.getOption('mode'))
})
}
export default CodeMirror

View File

@@ -16,7 +16,7 @@ export function trackEvent(category, action, label, value) {
}; };
// if online, load after sometime // if online, load after sometime
if (navigator.onLine && !window.DEBUG) { if (false && navigator.onLine && !window.DEBUG) {
/* eslint-disable */ /* eslint-disable */
// prettier-ignore // prettier-ignore

View File

@@ -1,10 +1,12 @@
import { h, Component } from 'preact'; import { h, Component } from 'preact';
import UserCodeMirror from './UserCodeMirror.jsx'; import UserCodeMirror from './UserCodeMirror.jsx';
import { computeHtml, computeCss, computeJs } from '../computes'; import { computeHtml, computeCss, computeJs } from '../computes';
import { HtmlModes, CssModes, JsModes } from '../codeModes'; import { modes, HtmlModes, CssModes, JsModes } from '../codeModes';
import { log, writeFile } from '../utils'; import { log, writeFile, loadJS } from '../utils';
import { SplitPane } from './SplitPane.jsx'; import { SplitPane } from './SplitPane.jsx';
import { trackEvent } from '../analytics'; import { trackEvent } from '../analytics';
import CodeMirror from '../CodeMirror';
import { deferred } from '../deferred';
const BASE_PATH = chrome.extension || window.DEBUG ? '/' : '/app'; const BASE_PATH = chrome.extension || window.DEBUG ? '/' : '/app';
const minCodeWrapSize = 33; const minCodeWrapSize = 33;
@@ -217,7 +219,9 @@ export default class ContentWrap extends Component {
currentCode.html === this.codeInPreview.html && currentCode.html === this.codeInPreview.html &&
currentCode.js === this.codeInPreview.js currentCode.js === this.codeInPreview.js
) { ) {
computeCss(currentCode.css, this.cssMode).then(function(css) { computeCss(currentCode.css, this.props.currentItem.cssMode).then(function(
css
) {
if (targetFrame.contentDocument.querySelector('#webmakerstyle')) { if (targetFrame.contentDocument.querySelector('#webmakerstyle')) {
targetFrame.contentDocument.querySelector( targetFrame.contentDocument.querySelector(
'#webmakerstyle' '#webmakerstyle'
@@ -225,9 +229,15 @@ export default class ContentWrap extends Component {
} }
}); });
} else { } else {
var htmlPromise = computeHtml(currentCode.html, this.htmlMode); var htmlPromise = computeHtml(
var cssPromise = computeCss(currentCode.css, this.cssMode); currentCode.html,
var jsPromise = computeJs(currentCode.js, this.jsMode); this.props.currentItem.htmlMode
);
var cssPromise = computeCss(
currentCode.css,
this.props.currentItem.cssMode
);
var jsPromise = computeJs(currentCode.js, this.props.currentItem.jsMode);
Promise.all([htmlPromise, cssPromise, jsPromise]).then(result => { Promise.all([htmlPromise, cssPromise, jsPromise]).then(result => {
this.createPreviewFile(result[0], result[1], result[2]); this.createPreviewFile(result[0], result[1], result[2]);
}); });
@@ -237,10 +247,15 @@ export default class ContentWrap extends Component {
this.codeInPreview.css = currentCode.css; this.codeInPreview.css = currentCode.css;
this.codeInPreview.js = currentCode.js; this.codeInPreview.js = currentCode.js;
} }
isValidItem(item) {
return !!item.title;
}
componentDidUpdate() { componentDidUpdate() {
this.refreshEditor(); log('🚀', 'didupdate', this.props.currentItem);
// this.setPreviewContent(true);
// console.log('componentdidupdate', this.props.currentItem); // if (this.isValidItem(this.props.currentItem)) {
// this.refreshEditor();
// }
} }
componentDidMount() { componentDidMount() {
this.props.onRef(this); this.props.onRef(this);
@@ -257,7 +272,13 @@ export default class ContentWrap extends Component {
this.cm.css.refresh(); this.cm.css.refresh();
this.cm.js.refresh(); this.cm.js.refresh();
this.setPreviewContent(true); // Set preview only when all modes are updated so that preview doesn't generate on partially
// correct modes and also doesn't happen 3 times.
Promise.all([
this.updateHtmlMode(this.props.currentItem.htmlMode),
this.updateCssMode(this.props.currentItem.cssMode),
this.updateJsMode(this.props.currentItem.jsMode)
]).then(() => this.setPreviewContent(true));
} }
applyCodemirrorSettings(prefs) { applyCodemirrorSettings(prefs) {
if (!this.cm) { if (!this.cm) {
@@ -404,7 +425,116 @@ export default class ContentWrap extends Component {
this.updateCodeWrapCollapseStates(); this.updateCodeWrapCollapseStates();
document.body.classList.remove('is-dragging'); document.body.classList.remove('is-dragging');
} }
/**
* Loaded the code comiler based on the mode selected
*/
handleModeRequirements(mode) {
const baseTranspilerPath = 'lib/transpilers';
// Exit if already loaded
var d = deferred();
if (modes[mode].hasLoaded) {
d.resolve();
return d.promise;
}
function setLoadedFlag() {
modes[mode].hasLoaded = true;
d.resolve();
}
if (mode === HtmlModes.JADE) {
loadJS(`${baseTranspilerPath}/jade.js`).then(setLoadedFlag);
} else if (mode === HtmlModes.MARKDOWN) {
loadJS(`${baseTranspilerPath}/marked.js`).then(setLoadedFlag);
} else if (mode === CssModes.LESS) {
loadJS(`${baseTranspilerPath}/less.min.js`).then(setLoadedFlag);
} else if (mode === CssModes.SCSS || mode === CssModes.SASS) {
loadJS(`${baseTranspilerPath}/sass.js`).then(function() {
window.sass = new Sass(`${baseTranspilerPath}/sass.worker.js`);
setLoadedFlag();
});
} else if (mode === CssModes.STYLUS) {
loadJS(`${baseTranspilerPath}/stylus.min.js`).then(setLoadedFlag);
} else if (mode === CssModes.ACSS) {
loadJS(`${baseTranspilerPath}/atomizer.browser.js`).then(setLoadedFlag);
} else if (mode === JsModes.COFFEESCRIPT) {
loadJS(`${baseTranspilerPath}/coffee-script.js`).then(setLoadedFlag);
} else if (mode === JsModes.ES6) {
loadJS(`${baseTranspilerPath}/babel.min.js`).then(setLoadedFlag);
} else if (mode === JsModes.TS) {
loadJS(`${baseTranspilerPath}/typescript.js`).then(setLoadedFlag);
} else {
d.resolve();
}
return d.promise;
}
updateHtmlMode(value) {
this.props.onCodeModeChange('html', value);
this.props.currentItem.htmlMode = value;
const htmlModeLabel = $('#js-html-mode-label');
htmlModeLabel.textContent = modes[value].label;
// FIXME - use a better selector for the mode selectbox
htmlModeLabel.parentElement.querySelector('select').value = value;
this.cm.html.setOption('mode', modes[value].cmMode);
CodeMirror.autoLoadMode(
this.cm.html,
modes[value].cmPath || modes[value].cmMode
);
return this.handleModeRequirements(value);
}
updateCssMode(value) {
this.props.onCodeModeChange('css', value);
this.props.currentItem.cssMode = value;
const cssModeLabel = $('#js-css-mode-label');
cssModeLabel.textContent = modes[value].label;
// FIXME - use a better selector for the mode selectbox
cssModeLabel.parentElement.querySelector('select').value = value;
this.cm.css.setOption('mode', modes[value].cmMode);
this.cm.css.setOption('readOnly', modes[value].cmDisable);
// cssSettingsBtn.classList[modes[value].hasSettings ? 'remove' : 'add'](
// 'hide'
// );
CodeMirror.autoLoadMode(
this.cm.css,
modes[value].cmPath || modes[value].cmMode
);
return this.handleModeRequirements(value);
}
updateJsMode(value) {
this.props.onCodeModeChange('js', value);
this.props.currentItem.jsMode = value;
const jsModeLabel = $('#js-js-mode-label');
jsModeLabel.textContent = modes[value].label;
// FIXME - use a better selector for the mode selectbox
jsModeLabel.parentElement.querySelector('select').value = value;
this.cm.js.setOption('mode', modes[value].cmMode);
CodeMirror.autoLoadMode(
this.cm.js,
modes[value].cmPath || modes[value].cmMode
);
return this.handleModeRequirements(value);
}
codeModeChangeHandler(e) {
var mode = e.target.value;
var type = e.target.dataset.type;
var currentMode = this.props.currentItem[
type === 'html' ? 'htmlMode' : type === 'css' ? 'cssMode' : 'jsMode'
];
if (currentMode !== mode) {
if (type === 'html') {
this.updateHtmlMode(mode).then(() => this.setPreviewContent(true));
} else if (type === 'js') {
this.updateJsMode(mode).then(() => this.setPreviewContent(true));
} else if (type === 'css') {
this.updateCssMode(mode).then(() => this.setPreviewContent(true));
}
trackEvent('ui', 'updateCodeMode', mode);
}
}
render() { render() {
return ( return (
<SplitPane <SplitPane
@@ -449,7 +579,7 @@ export default class ContentWrap extends Component {
<select <select
data-type="html" data-type="html"
class="js-mode-select hidden-select" class="js-mode-select hidden-select"
name="" onChange={this.codeModeChangeHandler.bind(this)}
> >
<option value="html">HTML</option> <option value="html">HTML</option>
<option value="markdown">Markdown</option> <option value="markdown">Markdown</option>
@@ -494,7 +624,7 @@ export default class ContentWrap extends Component {
<select <select
data-type="css" data-type="css"
class="js-mode-select hidden-select" class="js-mode-select hidden-select"
name="" onChange={this.codeModeChangeHandler.bind(this)}
> >
<option value="css">CSS</option> <option value="css">CSS</option>
<option value="scss">SCSS</option> <option value="scss">SCSS</option>
@@ -552,7 +682,11 @@ export default class ContentWrap extends Component {
JS JS
</span> </span>
<span class="caret" /> <span class="caret" />
<select data-type="js" class="js-mode-select hidden-select"> <select
data-type="js"
class="js-mode-select hidden-select"
onChange={this.codeModeChangeHandler.bind(this)}
>
<option value="js">JS</option> <option value="js">JS</option>
<option value="coffee">CoffeeScript</option> <option value="coffee">CoffeeScript</option>
<option value="es6">ES6 (Babel)</option> <option value="es6">ES6 (Babel)</option>

View File

@@ -1,5 +1,5 @@
import { h, Component } from 'preact'; import { h, Component } from 'preact';
import CodeMirror from 'codemirror'; import CodeMirror from '../CodeMirror';
import 'codemirror/addon/edit/matchbrackets.js'; import 'codemirror/addon/edit/matchbrackets.js';
import 'codemirror/addon/edit/matchtags.js'; import 'codemirror/addon/edit/matchtags.js';

View File

@@ -14,7 +14,7 @@ import { itemService } from '../itemService';
import '../db'; import '../db';
import Notifications from './Notifications'; import Notifications from './Notifications';
import Settings from './Settings.jsx'; import Settings from './Settings.jsx';
import { modes, cssModes } from '../codeModes'; import { modes, HtmlModes, CssModes, JsModes } from '../codeModes';
import { trackEvent } from '../analytics'; import { trackEvent } from '../analytics';
import { deferred } from '../deferred'; import { deferred } from '../deferred';
import { alertsService } from '../notifications'; import { alertsService } from '../notifications';
@@ -144,22 +144,21 @@ export default class App extends Component {
db.getSettings(this.defaultSettings).then(result => { db.getSettings(this.defaultSettings).then(result => {
if (result.preserveLastCode && lastCode) { if (result.preserveLastCode && lastCode) {
this.setState({ unsavedEditCount: 0 }); this.setState({ unsavedEditCount: 0 });
log('🚀', 'unsaededitcount setstate');
// For web app environment we don't fetch item from localStorage, // For web app environment we don't fetch item from localStorage,
// because the item isn't stored in the localStorage. // because the item isn't stored in the localStorage.
if (lastCode.id && window.IS_EXTENSION) { if (lastCode.id && window.IS_EXTENSION) {
db.local.get(lastCode.id, itemResult => { db.local.get(lastCode.id, itemResult => {
if (itemResult[lastCode.id]) { if (itemResult[lastCode.id]) {
log('Load item ', lastCode.id); log('Load item ', lastCode.id);
this.state.currentItem = itemResult[lastCode.id]; this.setCurrentItem(itemResult[lastCode.id]).then(() =>
this.refreshEditor(); this.refreshEditor()
this.setState({ currentItem: this.state.currentItem }); );
} }
}); });
} else { } else {
log('Load last unsaved item', lastCode); log('Load last unsaved item', lastCode);
this.state.currentItem = lastCode; this.setCurrentItem(lastCode).then(() => this.refreshEditor());
this.refreshEditor();
this.setState({ currentItem: this.state.currentItem });
} }
} else { } else {
this.createNewItem(); this.createNewItem();
@@ -181,7 +180,7 @@ export default class App extends Component {
this.toggleLayout( this.toggleLayout(
this.state.currentItem.layoutMode || this.state.prefs.layoutMode this.state.currentItem.layoutMode || this.state.prefs.layoutMode
); );
// this.contentWrap.refreshEditor(); this.contentWrap.refreshEditor();
} }
// Creates a new item with passed item's contents // Creates a new item with passed item's contents
forkItem(sourceItem) { forkItem(sourceItem) {
@@ -197,8 +196,7 @@ export default class App extends Component {
delete fork.id; delete fork.id;
fork.title = '(Forked) ' + sourceItem.title; fork.title = '(Forked) ' + sourceItem.title;
fork.updatedOn = Date.now(); fork.updatedOn = Date.now();
this.setCurrentItem(fork); this.setCurrentItem(fork).then(() => this.refreshEditor());
this.refreshEditor();
alertsService.add(`"${sourceItem.title}" was forked`); alertsService.add(`"${sourceItem.title}" was forked`);
trackEvent('fn', 'itemForked'); trackEvent('fn', 'itemForked');
} }
@@ -219,15 +217,11 @@ export default class App extends Component {
js: '', js: '',
externalLibs: { js: '', css: '' }, externalLibs: { js: '', css: '' },
layoutMode: this.state.currentLayoutMode layoutMode: this.state.currentLayoutMode
}); }).then(() => this.refreshEditor());
this.refreshEditor();
alertsService.add('New item created'); alertsService.add('New item created');
} }
openItem(item) { openItem(item) {
// console.log(itemId, this.state.savedItems) this.setCurrentItem(item).then(() => this.refreshEditor());
this.setCurrentItem(item);
this.refreshEditor();
alertsService.add('Saved item loaded'); alertsService.add('Saved item loaded');
} }
removeItem(itemId) { removeItem(itemId) {
@@ -259,14 +253,21 @@ export default class App extends Component {
trackEvent('fn', 'itemRemoved'); trackEvent('fn', 'itemRemoved');
} }
setCurrentItem(item) { setCurrentItem(item) {
this.setState({ currentItem: item }); const d = deferred();
log('Current Item set', item); // TODO: remove later
item.htmlMode =
item.htmlMode || this.state.prefs.htmlMode || HtmlModes.HTML;
item.cssMode = item.cssMode || this.state.prefs.cssMode || CssModes.CSS;
item.jsMode = item.jsMode || this.state.prefs.jsMode || JsModes.JS;
this.setState({ currentItem: item }, d.resolve);
log('🚀', 'currentItem setstate', item);
// Reset auto-saving flag // Reset auto-saving flag
this.isAutoSavingEnabled = false; this.isAutoSavingEnabled = false;
// Reset unsaved count, in UI also. // Reset unsaved count, in UI also.
this.setState({ unsavedEditCount: 0 }); this.setState({ unsavedEditCount: 0 });
// saveBtn.classList.remove('is-marked'); return d.promise;
} }
saveBtnClickHandler() { saveBtnClickHandler() {
trackEvent( trackEvent(
@@ -462,7 +463,7 @@ export default class App extends Component {
this.setState({ this.setState({
currentItem: { ...this.state.currentItem } currentItem: { ...this.state.currentItem }
}); });
this.contentWrap.setPreviewContent(true); // this.contentWrap.setPreviewContent(true);
alertsService.add('Libraries updated.'); alertsService.add('Libraries updated.');
} }
updateExternalLibCount() { updateExternalLibCount() {
@@ -593,6 +594,11 @@ export default class App extends Component {
itemService.setItemForUser(this.state.currentItem.id); itemService.setItemForUser(this.state.currentItem.id);
} }
} }
onCodeModeChange(ofWhat, mode) {
const item = {...this.state.currentItem}
item[`${ofWhat}Mode`] = mode;
this.setState({currentItem: item});
}
onCodeChange(type, code, isUserChange) { onCodeChange(type, code, isUserChange) {
this.state.currentItem[type] = code; this.state.currentItem[type] = code;
if (isUserChange) { if (isUserChange) {
@@ -760,6 +766,7 @@ export default class App extends Component {
currentLayoutMode={this.state.currentLayoutMode} currentLayoutMode={this.state.currentLayoutMode}
currentItem={this.state.currentItem} currentItem={this.state.currentItem}
onCodeChange={this.onCodeChange.bind(this)} onCodeChange={this.onCodeChange.bind(this)}
onCodeModeChange={this.onCodeModeChange.bind(this)}
onRef={comp => (this.contentWrap = comp)} onRef={comp => (this.contentWrap = comp)}
prefs={this.state.prefs} prefs={this.state.prefs}
/> />

View File

@@ -14,7 +14,7 @@ export function computeHtml(code, mode) {
if (mode === HtmlModes.HTML) { if (mode === HtmlModes.HTML) {
d.resolve(code); d.resolve(code);
} else if (mode === HtmlModes.MARKDOWN) { } else if (mode === HtmlModes.MARKDOWN) {
d.resolve(marked ? marked(code) : code); d.resolve(window.marked ? marked(code) : code);
} else if (mode === HtmlModes.JADE) { } else if (mode === HtmlModes.JADE) {
d.resolve(window.jade ? jade.render(code) : code); d.resolve(window.jade ? jade.render(code) : code);
} }
@@ -28,10 +28,10 @@ export function computeCss(code, mode) {
if (mode === CssModes.CSS) { if (mode === CssModes.CSS) {
d.resolve(code); d.resolve(code);
} else if (mode === CssModes.SCSS || mode === CssModes.SASS) { } else if (mode === CssModes.SCSS || mode === CssModes.SASS) {
if (sass && code) { if (window.sass && code) {
sass.compile( window.sass.compile(
code, { code, {
indentedSyntax: cssMode === CssModes.SASS indentedSyntax: mode === CssModes.SASS
}, },
function (result) { function (result) {
// Something was wrong // Something was wrong

View File

@@ -1,6 +1,10 @@
import { import {
trackEvent trackEvent
} from './analytics'; } from './analytics';
import {
deferred
} from './deferred';
window.DEBUG = document.cookie.indexOf('wmdebug') > -1; window.DEBUG = document.cookie.indexOf('wmdebug') > -1;
window.$ = document.querySelector.bind(document); window.$ = document.querySelector.bind(document);
@@ -80,7 +84,7 @@
export function log() { export function log() {
if (window.DEBUG) { if (window.DEBUG) {
console.log(...arguments); console.log(Date.now(), ...arguments);
} }
} }
@@ -273,6 +277,19 @@
); );
} }
export function loadJS(src) {
var d = deferred();
var ref = window.document.getElementsByTagName('script')[0];
var script = window.document.createElement('script');
script.src = src;
script.async = true;
ref.parentNode.insertBefore(script, ref);
script.onload = function () {
d.resolve();
};
return d.promise;
};
window.chrome = window.chrome || {}; window.chrome = window.chrome || {};
window.chrome.i18n = { window.chrome.i18n = {
getMessage: () => {} getMessage: () => {}