mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[FIX] playground: keep separate sessions for each tab
also, small refactoring closes #103
This commit is contained in:
+121
-97
@@ -1,5 +1,8 @@
|
|||||||
import { SAMPLES } from "./samples.js";
|
import { SAMPLES } from "./samples.js";
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Constants, helpers, utils
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
let owlJS;
|
let owlJS;
|
||||||
|
|
||||||
async function owlSourceCode() {
|
async function owlSourceCode() {
|
||||||
@@ -58,6 +61,92 @@ while True:
|
|||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an iframe, with all the js, css and xml properly injected.
|
||||||
|
*/
|
||||||
|
function makeCodeIframe(js, css, xml, errorHandler) {
|
||||||
|
// check templates
|
||||||
|
var qweb = new owl.QWeb();
|
||||||
|
const sanitizedXML = xml.replace(/<!--[\s\S]*?-->/g, "");
|
||||||
|
|
||||||
|
// will throw error if there is something wrong with xml
|
||||||
|
qweb.addTemplates(sanitizedXML);
|
||||||
|
|
||||||
|
// create iframe
|
||||||
|
const iframe = document.createElement("iframe");
|
||||||
|
|
||||||
|
iframe.onload = () => {
|
||||||
|
const doc = iframe.contentDocument;
|
||||||
|
// inject js
|
||||||
|
const owlScript = doc.createElement("script");
|
||||||
|
owlScript.type = "text/javascript";
|
||||||
|
owlScript.src = "../owl.js";
|
||||||
|
owlScript.addEventListener("load", () => {
|
||||||
|
const script = doc.createElement("script");
|
||||||
|
script.type = "text/javascript";
|
||||||
|
const content = `window.TEMPLATES = \`${sanitizedXML}\`\n${js}`;
|
||||||
|
script.innerHTML = content;
|
||||||
|
iframe.contentWindow.addEventListener("error", errorHandler);
|
||||||
|
iframe.contentWindow.addEventListener("unhandledrejection", errorHandler);
|
||||||
|
setTimeout(function() {
|
||||||
|
if (iframe.contentWindow) {
|
||||||
|
iframe.contentWindow.removeEventListener("error", errorHandler);
|
||||||
|
iframe.contentWindow.removeEventListener(
|
||||||
|
"unhandledrejection",
|
||||||
|
errorHandler
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
doc.body.appendChild(script);
|
||||||
|
});
|
||||||
|
doc.head.appendChild(owlScript);
|
||||||
|
|
||||||
|
// inject css
|
||||||
|
const style = document.createElement("style");
|
||||||
|
style.innerHTML = css;
|
||||||
|
doc.head.appendChild(style);
|
||||||
|
};
|
||||||
|
return iframe;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a zip file containing a functioning application
|
||||||
|
*/
|
||||||
|
async function makeApp(js, css, xml) {
|
||||||
|
await owl.utils.loadJS("libs/jszip.min.js");
|
||||||
|
|
||||||
|
const zip = new JSZip();
|
||||||
|
const processedJS = js
|
||||||
|
.split("\n")
|
||||||
|
.map(l => (l === "" ? "" : " " + l))
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
const JS = `async function startApp() {
|
||||||
|
// Loading templates
|
||||||
|
let TEMPLATES;
|
||||||
|
try {
|
||||||
|
TEMPLATES = await owl.utils.loadTemplates('app.xml');
|
||||||
|
} catch(e) {
|
||||||
|
document.write(\`This app requires a static server. If you have python installed, try 'python app.py'\`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Application code
|
||||||
|
${processedJS}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for DOM ready before starting
|
||||||
|
owl.utils.whenReady(startApp);`;
|
||||||
|
|
||||||
|
zip.file("app.js", JS);
|
||||||
|
zip.file("app.css", css);
|
||||||
|
zip.file("app.py", APP_PY);
|
||||||
|
zip.file("app.xml", xml);
|
||||||
|
zip.file("index.html", DEFAULT_HTML);
|
||||||
|
zip.file("owl.js", owlSourceCode());
|
||||||
|
return zip.generateAsync({ type: "blob" });
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// MAIN APP
|
// MAIN APP
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -94,68 +183,26 @@ class App extends owl.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async runCode() {
|
runCode() {
|
||||||
this.state.displayWelcome = false;
|
this.state.displayWelcome = false;
|
||||||
// check templates
|
let subiframe;
|
||||||
var qweb = new owl.QWeb();
|
let error = false;
|
||||||
var error = false;
|
const errorHandler = e => this.displayError(e.message || e.reason.message);
|
||||||
const sanitizedXML = this.state.xml.replace(/<!--[\s\S]*?-->/g, "");
|
|
||||||
try {
|
try {
|
||||||
qweb.addTemplates(sanitizedXML);
|
const { js, css, xml } = this.state;
|
||||||
|
subiframe = makeCodeIframe(js, css, xml, errorHandler);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
//probably problem with the templates
|
||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
this.displayError(error.message);
|
this.displayError(error.message);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
this.state.error = false;
|
this.state.error = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create iframe
|
|
||||||
const iframe = document.createElement("iframe");
|
|
||||||
|
|
||||||
iframe.onload = () => {
|
|
||||||
const doc = iframe.contentDocument;
|
|
||||||
// inject js
|
|
||||||
const owlScript = doc.createElement("script");
|
|
||||||
owlScript.type = "text/javascript";
|
|
||||||
owlScript.src = "../owl.js";
|
|
||||||
owlScript.addEventListener("load", () => {
|
|
||||||
const script = doc.createElement("script");
|
|
||||||
script.type = "text/javascript";
|
|
||||||
const content = `window.TEMPLATES = \`${sanitizedXML}\`\n${
|
|
||||||
this.state.js
|
|
||||||
}`;
|
|
||||||
script.innerHTML = content;
|
|
||||||
const errorHandler = e =>
|
|
||||||
this.displayError(e.message || e.reason.message);
|
|
||||||
iframe.contentWindow.addEventListener("error", errorHandler);
|
|
||||||
iframe.contentWindow.addEventListener(
|
|
||||||
"unhandledrejection",
|
|
||||||
errorHandler
|
|
||||||
);
|
|
||||||
setTimeout(function() {
|
|
||||||
if (iframe.contentWindow) {
|
|
||||||
iframe.contentWindow.removeEventListener("error", errorHandler);
|
|
||||||
iframe.contentWindow.removeEventListener(
|
|
||||||
"unhandledrejection",
|
|
||||||
errorHandler
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, 200);
|
|
||||||
doc.body.appendChild(script);
|
|
||||||
});
|
|
||||||
doc.head.appendChild(owlScript);
|
|
||||||
|
|
||||||
// inject css
|
|
||||||
const style = document.createElement("style");
|
|
||||||
style.innerHTML = this.state.css;
|
|
||||||
doc.head.appendChild(style);
|
|
||||||
};
|
|
||||||
this.refs.content.innerHTML = "";
|
this.refs.content.innerHTML = "";
|
||||||
this.refs.content.appendChild(iframe);
|
this.refs.content.appendChild(subiframe);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSample(ev) {
|
setSample(ev) {
|
||||||
@@ -212,40 +259,10 @@ class App extends owl.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async downloadCode() {
|
async downloadCode() {
|
||||||
|
const { js, css, xml } = this.state;
|
||||||
|
const content = await makeApp(js, css, xml);
|
||||||
await owl.utils.loadJS("libs/FileSaver.min.js");
|
await owl.utils.loadJS("libs/FileSaver.min.js");
|
||||||
await owl.utils.loadJS("libs/jszip.min.js");
|
saveAs(content, "app.zip");
|
||||||
|
|
||||||
const zip = new JSZip();
|
|
||||||
|
|
||||||
const JS = `async function startApp() {
|
|
||||||
// Loading templates
|
|
||||||
let TEMPLATES;
|
|
||||||
try {
|
|
||||||
TEMPLATES = await owl.utils.loadTemplates('app.xml');
|
|
||||||
} catch(e) {
|
|
||||||
document.write(\`This app requires a static server. If you have python installed, try 'python app.py'\`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Application code
|
|
||||||
${this.state.js
|
|
||||||
.split("\n")
|
|
||||||
.map(l => (l === "" ? "" : " " + l))
|
|
||||||
.join("\n")}
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for DOM ready before starting
|
|
||||||
owl.utils.whenReady(startApp);`;
|
|
||||||
|
|
||||||
zip.file("app.js", JS);
|
|
||||||
zip.file("app.css", this.state.css);
|
|
||||||
zip.file("app.py", APP_PY);
|
|
||||||
zip.file("app.xml", this.state.xml);
|
|
||||||
zip.file("index.html", DEFAULT_HTML);
|
|
||||||
zip.file("owl.js", owlSourceCode());
|
|
||||||
zip.generateAsync({ type: "blob" }).then(function(content) {
|
|
||||||
saveAs(content, "app.zip");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,17 +276,26 @@ class TabbedEditor extends owl.Component {
|
|||||||
currentTab: props.js ? "js" : props.xml ? "xml" : "css"
|
currentTab: props.js ? "js" : props.xml ? "xml" : "css"
|
||||||
};
|
};
|
||||||
this.setTab = owl.utils.debounce(this.setTab, 250, true);
|
this.setTab = owl.utils.debounce(this.setTab, 250, true);
|
||||||
|
|
||||||
|
this.sessions = {};
|
||||||
|
for (let tab of ["js", "xml", "css"]) {
|
||||||
|
if (props[tab]) {
|
||||||
|
this.sessions[tab] = new ace.EditSession(props[tab], MODES[tab]);
|
||||||
|
this.sessions[tab].setOption("useWorker", false);
|
||||||
|
const tabSize = tab === "xml" ? 2 : 4;
|
||||||
|
this.sessions[tab].setOption("tabSize", tabSize);
|
||||||
|
this.sessions[tab].setUndoManager(new ace.UndoManager());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.editor = ace.edit(this.refs.editor);
|
this.editor = ace.edit(this.refs.editor);
|
||||||
|
|
||||||
// remove this for xml/css (?)
|
|
||||||
this.editor.session.setOption("useWorker", false);
|
|
||||||
this.editor.setValue(this.props[this.state.currentTab], -1);
|
this.editor.setValue(this.props[this.state.currentTab], -1);
|
||||||
this.editor.setFontSize("12px");
|
this.editor.setFontSize("12px");
|
||||||
this.editor.setTheme("ace/theme/monokai");
|
this.editor.setTheme("ace/theme/monokai");
|
||||||
this.editor.session.setMode(MODES[this.state.currentTab]);
|
this.editor.setSession(this.sessions[this.state.currentTab]);
|
||||||
const tabSize = this.state.currentTab === "xml" ? 2 : 4;
|
const tabSize = this.state.currentTab === "xml" ? 2 : 4;
|
||||||
this.editor.session.setOption("tabSize", tabSize);
|
this.editor.session.setOption("tabSize", tabSize);
|
||||||
this.editor.on("blur", () => {
|
this.editor.on("blur", () => {
|
||||||
@@ -285,10 +311,9 @@ class TabbedEditor extends owl.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
patched() {
|
patched() {
|
||||||
if (this.editor) {
|
const session = this.sessions[this.state.currentTab];
|
||||||
window.dispatchEvent(new Event("resize"));
|
session.setValue(this.props[this.state.currentTab], -1);
|
||||||
this.editor.setValue(this.props[this.state.currentTab], -1);
|
this.editor.setSession(session);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
willUnmount() {
|
willUnmount() {
|
||||||
@@ -297,13 +322,12 @@ class TabbedEditor extends owl.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setTab(tab) {
|
setTab(tab) {
|
||||||
this.editor.setValue(this.props[tab], -1);
|
if (this.state.currentTab !== tab) {
|
||||||
|
this.state.currentTab = tab;
|
||||||
const mode = MODES[tab];
|
const session = this.sessions[this.state.currentTab];
|
||||||
this.editor.session.setMode(mode);
|
session.doc.setValue(this.props[tab], -1);
|
||||||
const tabSize = tab === "xml" ? 2 : 4;
|
this.editor.setSession(session);
|
||||||
this.editor.session.setOption("tabSize", tabSize);
|
}
|
||||||
this.state.currentTab = tab;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseDown(ev) {
|
onMouseDown(ev) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div t-name="TabbedEditor" class="tabbed-editor">
|
<div t-name="TabbedEditor" class="tabbed-editor">
|
||||||
<div class="tabBar" t-att-class="{resizeable: props.resizeable}" t-on-mousedown="onMouseDown">
|
<div class="tabBar" t-att-class="{resizeable: props.resizeable}" t-on-mousedown="onMouseDown">
|
||||||
<t t-foreach="['js', 'xml', 'css']" t-as="tab">
|
<t t-foreach="['js', 'xml', 'css']" t-as="tab">
|
||||||
<a t-if="props[tab]" class="tab flash" t-att-class="{active: state.currentTab===tab}" t-on-click="setTab(tab)">
|
<a t-ref="tab" t-if="props[tab]" class="tab flash" t-att-class="{active: state.currentTab===tab}" t-on-click="setTab(tab)">
|
||||||
<t t-esc="tab"/>
|
<t t-esc="tab"/>
|
||||||
</a>
|
</a>
|
||||||
</t>
|
</t>
|
||||||
|
|||||||
Reference in New Issue
Block a user