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:
60
webmaker/src/CodeMirror.js
Normal file
60
webmaker/src/CodeMirror.js
Normal 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
|
@@ -16,7 +16,7 @@ export function trackEvent(category, action, label, value) {
|
||||
};
|
||||
|
||||
// if online, load after sometime
|
||||
if (navigator.onLine && !window.DEBUG) {
|
||||
if (false && navigator.onLine && !window.DEBUG) {
|
||||
/* eslint-disable */
|
||||
|
||||
// prettier-ignore
|
||||
|
@@ -1,10 +1,12 @@
|
||||
import { h, Component } from 'preact';
|
||||
import UserCodeMirror from './UserCodeMirror.jsx';
|
||||
import { computeHtml, computeCss, computeJs } from '../computes';
|
||||
import { HtmlModes, CssModes, JsModes } from '../codeModes';
|
||||
import { log, writeFile } from '../utils';
|
||||
import { modes, HtmlModes, CssModes, JsModes } from '../codeModes';
|
||||
import { log, writeFile, loadJS } from '../utils';
|
||||
import { SplitPane } from './SplitPane.jsx';
|
||||
import { trackEvent } from '../analytics';
|
||||
import CodeMirror from '../CodeMirror';
|
||||
import { deferred } from '../deferred';
|
||||
|
||||
const BASE_PATH = chrome.extension || window.DEBUG ? '/' : '/app';
|
||||
const minCodeWrapSize = 33;
|
||||
@@ -217,7 +219,9 @@ export default class ContentWrap extends Component {
|
||||
currentCode.html === this.codeInPreview.html &&
|
||||
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')) {
|
||||
targetFrame.contentDocument.querySelector(
|
||||
'#webmakerstyle'
|
||||
@@ -225,9 +229,15 @@ export default class ContentWrap extends Component {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var htmlPromise = computeHtml(currentCode.html, this.htmlMode);
|
||||
var cssPromise = computeCss(currentCode.css, this.cssMode);
|
||||
var jsPromise = computeJs(currentCode.js, this.jsMode);
|
||||
var htmlPromise = computeHtml(
|
||||
currentCode.html,
|
||||
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 => {
|
||||
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.js = currentCode.js;
|
||||
}
|
||||
isValidItem(item) {
|
||||
return !!item.title;
|
||||
}
|
||||
componentDidUpdate() {
|
||||
this.refreshEditor();
|
||||
// this.setPreviewContent(true);
|
||||
// console.log('componentdidupdate', this.props.currentItem);
|
||||
log('🚀', 'didupdate', this.props.currentItem);
|
||||
|
||||
// if (this.isValidItem(this.props.currentItem)) {
|
||||
// this.refreshEditor();
|
||||
// }
|
||||
}
|
||||
componentDidMount() {
|
||||
this.props.onRef(this);
|
||||
@@ -257,7 +272,13 @@ export default class ContentWrap extends Component {
|
||||
this.cm.css.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) {
|
||||
if (!this.cm) {
|
||||
@@ -404,7 +425,116 @@ export default class ContentWrap extends Component {
|
||||
this.updateCodeWrapCollapseStates();
|
||||
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() {
|
||||
return (
|
||||
<SplitPane
|
||||
@@ -449,7 +579,7 @@ export default class ContentWrap extends Component {
|
||||
<select
|
||||
data-type="html"
|
||||
class="js-mode-select hidden-select"
|
||||
name=""
|
||||
onChange={this.codeModeChangeHandler.bind(this)}
|
||||
>
|
||||
<option value="html">HTML</option>
|
||||
<option value="markdown">Markdown</option>
|
||||
@@ -494,7 +624,7 @@ export default class ContentWrap extends Component {
|
||||
<select
|
||||
data-type="css"
|
||||
class="js-mode-select hidden-select"
|
||||
name=""
|
||||
onChange={this.codeModeChangeHandler.bind(this)}
|
||||
>
|
||||
<option value="css">CSS</option>
|
||||
<option value="scss">SCSS</option>
|
||||
@@ -552,7 +682,11 @@ export default class ContentWrap extends Component {
|
||||
JS
|
||||
</span>
|
||||
<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="coffee">CoffeeScript</option>
|
||||
<option value="es6">ES6 (Babel)</option>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { h, Component } from 'preact';
|
||||
import CodeMirror from 'codemirror';
|
||||
import CodeMirror from '../CodeMirror';
|
||||
|
||||
import 'codemirror/addon/edit/matchbrackets.js';
|
||||
import 'codemirror/addon/edit/matchtags.js';
|
||||
|
@@ -14,7 +14,7 @@ import { itemService } from '../itemService';
|
||||
import '../db';
|
||||
import Notifications from './Notifications';
|
||||
import Settings from './Settings.jsx';
|
||||
import { modes, cssModes } from '../codeModes';
|
||||
import { modes, HtmlModes, CssModes, JsModes } from '../codeModes';
|
||||
import { trackEvent } from '../analytics';
|
||||
import { deferred } from '../deferred';
|
||||
import { alertsService } from '../notifications';
|
||||
@@ -144,22 +144,21 @@ export default class App extends Component {
|
||||
db.getSettings(this.defaultSettings).then(result => {
|
||||
if (result.preserveLastCode && lastCode) {
|
||||
this.setState({ unsavedEditCount: 0 });
|
||||
log('🚀', 'unsaededitcount setstate');
|
||||
// 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 });
|
||||
this.setCurrentItem(itemResult[lastCode.id]).then(() =>
|
||||
this.refreshEditor()
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log('Load last unsaved item', lastCode);
|
||||
this.state.currentItem = lastCode;
|
||||
this.refreshEditor();
|
||||
this.setState({ currentItem: this.state.currentItem });
|
||||
this.setCurrentItem(lastCode).then(() => this.refreshEditor());
|
||||
}
|
||||
} else {
|
||||
this.createNewItem();
|
||||
@@ -181,7 +180,7 @@ export default class App extends Component {
|
||||
this.toggleLayout(
|
||||
this.state.currentItem.layoutMode || this.state.prefs.layoutMode
|
||||
);
|
||||
// this.contentWrap.refreshEditor();
|
||||
this.contentWrap.refreshEditor();
|
||||
}
|
||||
// Creates a new item with passed item's contents
|
||||
forkItem(sourceItem) {
|
||||
@@ -197,8 +196,7 @@ export default class App extends Component {
|
||||
delete fork.id;
|
||||
fork.title = '(Forked) ' + sourceItem.title;
|
||||
fork.updatedOn = Date.now();
|
||||
this.setCurrentItem(fork);
|
||||
this.refreshEditor();
|
||||
this.setCurrentItem(fork).then(() => this.refreshEditor());
|
||||
alertsService.add(`"${sourceItem.title}" was forked`);
|
||||
trackEvent('fn', 'itemForked');
|
||||
}
|
||||
@@ -219,15 +217,11 @@ export default class App extends Component {
|
||||
js: '',
|
||||
externalLibs: { js: '', css: '' },
|
||||
layoutMode: this.state.currentLayoutMode
|
||||
});
|
||||
this.refreshEditor();
|
||||
}).then(() => this.refreshEditor());
|
||||
alertsService.add('New item created');
|
||||
}
|
||||
openItem(item) {
|
||||
// console.log(itemId, this.state.savedItems)
|
||||
|
||||
this.setCurrentItem(item);
|
||||
this.refreshEditor();
|
||||
this.setCurrentItem(item).then(() => this.refreshEditor());
|
||||
alertsService.add('Saved item loaded');
|
||||
}
|
||||
removeItem(itemId) {
|
||||
@@ -259,14 +253,21 @@ export default class App extends Component {
|
||||
trackEvent('fn', 'itemRemoved');
|
||||
}
|
||||
setCurrentItem(item) {
|
||||
this.setState({ currentItem: item });
|
||||
log('Current Item set', item);
|
||||
const d = deferred();
|
||||
// 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
|
||||
this.isAutoSavingEnabled = false;
|
||||
// Reset unsaved count, in UI also.
|
||||
this.setState({ unsavedEditCount: 0 });
|
||||
// saveBtn.classList.remove('is-marked');
|
||||
return d.promise;
|
||||
}
|
||||
saveBtnClickHandler() {
|
||||
trackEvent(
|
||||
@@ -462,7 +463,7 @@ export default class App extends Component {
|
||||
this.setState({
|
||||
currentItem: { ...this.state.currentItem }
|
||||
});
|
||||
this.contentWrap.setPreviewContent(true);
|
||||
// this.contentWrap.setPreviewContent(true);
|
||||
alertsService.add('Libraries updated.');
|
||||
}
|
||||
updateExternalLibCount() {
|
||||
@@ -593,6 +594,11 @@ export default class App extends Component {
|
||||
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) {
|
||||
this.state.currentItem[type] = code;
|
||||
if (isUserChange) {
|
||||
@@ -760,6 +766,7 @@ export default class App extends Component {
|
||||
currentLayoutMode={this.state.currentLayoutMode}
|
||||
currentItem={this.state.currentItem}
|
||||
onCodeChange={this.onCodeChange.bind(this)}
|
||||
onCodeModeChange={this.onCodeModeChange.bind(this)}
|
||||
onRef={comp => (this.contentWrap = comp)}
|
||||
prefs={this.state.prefs}
|
||||
/>
|
||||
|
@@ -14,7 +14,7 @@ export function computeHtml(code, mode) {
|
||||
if (mode === HtmlModes.HTML) {
|
||||
d.resolve(code);
|
||||
} else if (mode === HtmlModes.MARKDOWN) {
|
||||
d.resolve(marked ? marked(code) : code);
|
||||
d.resolve(window.marked ? marked(code) : code);
|
||||
} else if (mode === HtmlModes.JADE) {
|
||||
d.resolve(window.jade ? jade.render(code) : code);
|
||||
}
|
||||
@@ -28,10 +28,10 @@ export function computeCss(code, mode) {
|
||||
if (mode === CssModes.CSS) {
|
||||
d.resolve(code);
|
||||
} else if (mode === CssModes.SCSS || mode === CssModes.SASS) {
|
||||
if (sass && code) {
|
||||
sass.compile(
|
||||
if (window.sass && code) {
|
||||
window.sass.compile(
|
||||
code, {
|
||||
indentedSyntax: cssMode === CssModes.SASS
|
||||
indentedSyntax: mode === CssModes.SASS
|
||||
},
|
||||
function (result) {
|
||||
// Something was wrong
|
||||
|
@@ -1,6 +1,10 @@
|
||||
import {
|
||||
trackEvent
|
||||
} from './analytics';
|
||||
|
||||
import {
|
||||
deferred
|
||||
} from './deferred';
|
||||
window.DEBUG = document.cookie.indexOf('wmdebug') > -1;
|
||||
|
||||
window.$ = document.querySelector.bind(document);
|
||||
@@ -80,7 +84,7 @@
|
||||
|
||||
export function log() {
|
||||
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.i18n = {
|
||||
getMessage: () => {}
|
||||
|
Reference in New Issue
Block a user