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 (navigator.onLine && !window.DEBUG) {
if (false && navigator.onLine && !window.DEBUG) {
/* eslint-disable */
// prettier-ignore

View File

@@ -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>

View File

@@ -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';

View File

@@ -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}
/>

View File

@@ -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

View File

@@ -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: () => {}