Whats new?
+
+
2.2.0
+
+ - Code Autocompletion - See code suggestions while you type!
+ - Full Screen Preview - Checkout your creation in a full-screen layout.
+ - SASS - SASS support added for CSS.
+ - Faster CSS update - Preview updates instantly without refresh when just CSS is changed.
+ - Bugfix - Indentation fixed while going on new line.
+ - Bugfix - Works even in Chrome Canary now. Though libraries can be added only through CDNs.
+ - Suggest a feature.
+ - Thank you for being a part of this awesome 4000+ developers community. If you find Web Maker helpful, Please rate Web Maker & share it.
+
+
+
2.1.0
@@ -289,7 +310,6 @@
- Code Folding - Collapse large code blocks for easy editing.
- Bugfix - Support JSX in JavaScript.
- Better onboarding for first time users.
- - Like it? Please rate Web Maker
@@ -361,6 +381,11 @@
+
+
+
+
+
diff --git a/src/lib/codemirror/mode/jsx/jsx.js b/src/lib/codemirror/mode/jsx/jsx.js
new file mode 100644
index 0000000..45c3024
--- /dev/null
+++ b/src/lib/codemirror/mode/jsx/jsx.js
@@ -0,0 +1,148 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"))
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript"], mod)
+ else // Plain browser env
+ mod(CodeMirror)
+})(function(CodeMirror) {
+ "use strict"
+
+ // Depth means the amount of open braces in JS context, in XML
+ // context 0 means not in tag, 1 means in tag, and 2 means in tag
+ // and js block comment.
+ function Context(state, mode, depth, prev) {
+ this.state = state; this.mode = mode; this.depth = depth; this.prev = prev
+ }
+
+ function copyContext(context) {
+ return new Context(CodeMirror.copyState(context.mode, context.state),
+ context.mode,
+ context.depth,
+ context.prev && copyContext(context.prev))
+ }
+
+ CodeMirror.defineMode("jsx", function(config, modeConfig) {
+ var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false})
+ var jsMode = CodeMirror.getMode(config, modeConfig && modeConfig.base || "javascript")
+
+ function flatXMLIndent(state) {
+ var tagName = state.tagName
+ state.tagName = null
+ var result = xmlMode.indent(state, "")
+ state.tagName = tagName
+ return result
+ }
+
+ function token(stream, state) {
+ if (state.context.mode == xmlMode)
+ return xmlToken(stream, state, state.context)
+ else
+ return jsToken(stream, state, state.context)
+ }
+
+ function xmlToken(stream, state, cx) {
+ if (cx.depth == 2) { // Inside a JS /* */ comment
+ if (stream.match(/^.*?\*\//)) cx.depth = 1
+ else stream.skipToEnd()
+ return "comment"
+ }
+
+ if (stream.peek() == "{") {
+ xmlMode.skipAttribute(cx.state)
+
+ var indent = flatXMLIndent(cx.state), xmlContext = cx.state.context
+ // If JS starts on same line as tag
+ if (xmlContext && stream.match(/^[^>]*>\s*$/, false)) {
+ while (xmlContext.prev && !xmlContext.startOfLine)
+ xmlContext = xmlContext.prev
+ // If tag starts the line, use XML indentation level
+ if (xmlContext.startOfLine) indent -= config.indentUnit
+ // Else use JS indentation level
+ else if (cx.prev.state.lexical) indent = cx.prev.state.lexical.indented
+ // Else if inside of tag
+ } else if (cx.depth == 1) {
+ indent += config.indentUnit
+ }
+
+ state.context = new Context(CodeMirror.startState(jsMode, indent),
+ jsMode, 0, state.context)
+ return null
+ }
+
+ if (cx.depth == 1) { // Inside of tag
+ if (stream.peek() == "<") { // Tag inside of tag
+ xmlMode.skipAttribute(cx.state)
+ state.context = new Context(CodeMirror.startState(xmlMode, flatXMLIndent(cx.state)),
+ xmlMode, 0, state.context)
+ return null
+ } else if (stream.match("//")) {
+ stream.skipToEnd()
+ return "comment"
+ } else if (stream.match("/*")) {
+ cx.depth = 2
+ return token(stream, state)
+ }
+ }
+
+ var style = xmlMode.token(stream, cx.state), cur = stream.current(), stop
+ if (/\btag\b/.test(style)) {
+ if (/>$/.test(cur)) {
+ if (cx.state.context) cx.depth = 0
+ else state.context = state.context.prev
+ } else if (/^ -1) {
+ stream.backUp(cur.length - stop)
+ }
+ return style
+ }
+
+ function jsToken(stream, state, cx) {
+ if (stream.peek() == "<" && jsMode.expressionAllowed(stream, cx.state)) {
+ jsMode.skipExpression(cx.state)
+ state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "")),
+ xmlMode, 0, state.context)
+ return null
+ }
+
+ var style = jsMode.token(stream, cx.state)
+ if (!style && cx.depth != null) {
+ var cur = stream.current()
+ if (cur == "{") {
+ cx.depth++
+ } else if (cur == "}") {
+ if (--cx.depth == 0) state.context = state.context.prev
+ }
+ }
+ return style
+ }
+
+ return {
+ startState: function() {
+ return {context: new Context(CodeMirror.startState(jsMode), jsMode)}
+ },
+
+ copyState: function(state) {
+ return {context: copyContext(state.context)}
+ },
+
+ token: token,
+
+ indent: function(state, textAfter, fullLine) {
+ return state.context.mode.indent(state.context.state, textAfter, fullLine)
+ },
+
+ innerMode: function(state) {
+ return state.context
+ }
+ }
+ }, "xml", "javascript")
+
+ CodeMirror.defineMIME("text/jsx", "jsx")
+ CodeMirror.defineMIME("text/typescript-jsx", {name: "jsx", base: {name: "javascript", typescript: true}})
+});
diff --git a/src/manifest.json b/src/manifest.json
index c323088..a138c84 100644
--- a/src/manifest.json
+++ b/src/manifest.json
@@ -8,7 +8,7 @@
"storage",
"tabs"
],
- "content_security_policy": "script-src 'self' https://www.google-analytics.com 'unsafe-eval'; object-src 'self'",
+ "content_security_policy": "script-src 'self' https://ajax.googleapis.com https://code.jquery.com https://cdnjs.cloudflare.com https://unpkg.com https://maxcdn.bootstrapcdn.com https://cdn.jsdelivr.net/ https://www.google-analytics.com 'unsafe-eval'; object-src 'self'",
"options_ui": {
"page": "options.html",
"chrome_style": true
diff --git a/src/script.js b/src/script.js
index b3084cf..f435c17 100644
--- a/src/script.js
+++ b/src/script.js
@@ -1,6 +1,6 @@
/* global trackEvent */
/* global layoutBtn1, layoutBtn2, layoutBtn3, helpModal, notificationsModal, addLibraryModal,
-onboardModal, layoutBtn1, layoutBtn2, layoutBtn3, helpBtn, onboardModal, onboardModal,
+onboardModal, layoutBtn1, layoutBtn2, layoutBtn3, layoutBtn4, helpBtn, onboardModal, onboardModal,
addLibraryModal, addLibraryModal, notificationsBtn, notificationsModal, notificationsModal,
notificationsModal, notificationsBtn, codepenBtn, saveHtmlBtn, openBtn, saveBtn, newBtn,
settingsBtn, onboardModal, notificationsBtn, onboardShowInTabOptionBtn, onboardDontShowInTabOptionBtn */
@@ -9,7 +9,7 @@ settingsBtn, onboardModal, notificationsBtn, onboardShowInTabOptionBtn, onboardD
/* eslint-enable no-extra-semi */
var scope = scope || {};
- var version = '2.1.0';
+ var version = '2.2.0';
if (window.DEBUG) {
window.scope = scope;
@@ -23,6 +23,7 @@ settingsBtn, onboardModal, notificationsBtn, onboardShowInTabOptionBtn, onboardD
var CssModes = {
CSS: 'css',
SCSS: 'scss',
+ SASS: 'sass',
LESS: 'less',
STYLUS: 'stylus'
};
@@ -39,10 +40,11 @@ settingsBtn, onboardModal, notificationsBtn, onboardShowInTabOptionBtn, onboardD
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', cmMode: 'javascript', codepenVal: 'typescript' };
- modes[CssModes.CSS] = { label: 'CSS', cmMode: 'css', codepenVal: 'none' };
- modes[CssModes.SCSS] = { label: 'SCSS', cmMode: 'sass', codepenVal: 'scss' };
- modes[CssModes.LESS] = { label: 'LESS', cmMode: 'text/x-less', codepenVal: 'less' };
+ 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' };
var updateTimer
@@ -60,6 +62,7 @@ settingsBtn, onboardModal, notificationsBtn, onboardShowInTabOptionBtn, onboardD
, codeSplitInstance
// TODO: for legacy reasons when. Will be refactored as global preferences.
, prefs = {}
+ , codeInPreview = { html: null, css: null, js: null }
// DOM nodes
, frame = $('#demo-frame')
@@ -137,7 +140,10 @@ settingsBtn, onboardModal, notificationsBtn, onboardShowInTabOptionBtn, onboardD
mainSplitInstance = Split(['#js-code-side', '#js-demo-side' ], {
direction: (currentLayoutMode === 2 ? 'vertical' : 'horizontal'),
minSize: 34,
- gutterSize: 6
+ gutterSize: 6,
+ onDragEnd: function () {
+ scope.setPreviewContent(true);
+ }
});
}
function toggleLayout(mode) {
@@ -151,19 +157,22 @@ settingsBtn, onboardModal, notificationsBtn, onboardShowInTabOptionBtn, onboardD
layoutBtn1.classList.remove('selected');
layoutBtn2.classList.remove('selected');
layoutBtn3.classList.remove('selected');
+ layoutBtn4.classList.remove('selected');
$('#layoutBtn' + mode).classList.add('selected');
document.body.classList.remove('layout-1');
document.body.classList.remove('layout-2');
document.body.classList.remove('layout-3');
+ document.body.classList.remove('layout-4');
document.body.classList.add('layout-' + mode);
resetSplitting();
+ scope.setPreviewContent(true);
}
function onExternalLibChange() {
utils.log('onExternalLibChange');
updateExternalLibUi();
- scope.setPreviewContent();
+ scope.setPreviewContent(true);
}
function updateExternalLibUi() {
@@ -388,7 +397,7 @@ settingsBtn, onboardModal, notificationsBtn, onboardShowInTabOptionBtn, onboardD
loadJS('lib/marked.js').then(setLoadedFlag);
} else if (mode === CssModes.LESS) {
loadJS('lib/less.min.js').then(setLoadedFlag);
- } else if (mode === CssModes.SCSS) {
+ } else if (mode === CssModes.SCSS || mode === CssModes.SASS) {
loadJS('lib/sass.js').then(function () {
sass = new Sass('lib/sass.worker.js');
setLoadedFlag();
@@ -409,7 +418,7 @@ settingsBtn, onboardModal, notificationsBtn, onboardShowInTabOptionBtn, onboardD
htmlModelLabel.textContent = modes[value].label;
handleModeRequirements(value);
scope.cm.html.setOption('mode', modes[value].cmMode);
- CodeMirror.autoLoadMode(scope.cm.html, modes[value].cmMode);
+ CodeMirror.autoLoadMode(scope.cm.html, modes[value].cmPath || modes[value].cmMode);
trackEvent('ui', 'updateCodeMode', 'html', value);
}
function updateCssMode(value) {
@@ -417,7 +426,7 @@ settingsBtn, onboardModal, notificationsBtn, onboardShowInTabOptionBtn, onboardD
cssModelLabel.textContent = modes[value].label;
handleModeRequirements(value);
scope.cm.css.setOption('mode', modes[value].cmMode);
- CodeMirror.autoLoadMode(scope.cm.css, modes[value].cmMode);
+ CodeMirror.autoLoadMode(scope.cm.css, modes[value].cmPath || modes[value].cmMode);
trackEvent('ui', 'updateCodeMode', 'css', value);
}
function updateJsMode(value) {
@@ -425,7 +434,7 @@ settingsBtn, onboardModal, notificationsBtn, onboardShowInTabOptionBtn, onboardD
jsModelLabel.textContent = modes[value].label;
handleModeRequirements(value);
scope.cm.js.setOption('mode', modes[value].cmMode);
- CodeMirror.autoLoadMode(scope.cm.js, modes[value].cmMode);
+ CodeMirror.autoLoadMode(scope.cm.js, modes[value].cmPath || modes[value].cmMode);
trackEvent('ui', 'updateCodeMode', 'js', value);
// FIXME: Will be saved as part of scope settings
/*
@@ -457,9 +466,9 @@ settingsBtn, onboardModal, notificationsBtn, onboardShowInTabOptionBtn, onboardD
if (cssMode === CssModes.CSS) {
d.resolve(code);
- } else if (cssMode === CssModes.SCSS) {
- sass.compile(code, function(result) {
- // Something as wrong
+ } else if (cssMode === CssModes.SCSS || cssMode === CssModes.SASS) {
+ sass.compile(code, { indentedSyntax: cssMode === CssModes.SASS }, function(result) {
+ // Something was wrong
if (result.line && result.message) {
showErrors('css', [ { lineNumber: result.line - 1, message: result.message } ]);
}
@@ -598,7 +607,7 @@ settingsBtn, onboardModal, notificationsBtn, onboardShowInTabOptionBtn, onboardD
});
}
- function getCompleteHtml(html, css, js) {
+ function getCompleteHtml(html, css) {
var externalJs = externalJsTextarea.value.split('\n').reduce(function (scripts, url) {
return scripts + (url ? '\n' : '');
}, '');
@@ -607,17 +616,47 @@ settingsBtn, onboardModal, notificationsBtn, onboardShowInTabOptionBtn, onboardD
}, '');
var contents = '\n\n'
+ externalCss + '\n'
- + '\n'
+ + '\n'
+ '\n'
+ '\n' + html + '\n'
- + externalJs + '\n\n';
+ + externalJs + '\n