refactored HTML injection system
- using a <div> container instead of an <iframe> (simplifies the build system, avoids the quirks iframes) - Bulma CSS is encapsulated in the #simulator ID to avoid polluting the rest of the page, as well as increasing the chances to override the page CSS
This commit is contained in:
parent
5f63dc7c7d
commit
3121342337
1508
simulator/package-lock.json
generated
1508
simulator/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -8,10 +8,11 @@
|
|||||||
"directory": "simulator"
|
"directory": "simulator"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "node tools/build.js"
|
"build": "node tools/build.js"
|
||||||
},
|
},
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"node-sass": "^6.0.1",
|
||||||
"purify-css": "^1.2.5",
|
"purify-css": "^1.2.5",
|
||||||
"typescript": "^4.4.3"
|
"typescript": "^4.4.3"
|
||||||
},
|
},
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
input[type=number] {
|
|
||||||
-moz-appearance:textfield;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wide-label .field-label {
|
|
||||||
flex-grow: 2.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown.is-fullwidth {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown.is-fullwidth .dropdown-trigger,
|
|
||||||
.dropdown.is-fullwidth .dropdown-menu {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-trigger.with-dropdown-icon::after {
|
|
||||||
border: 3px solid black;
|
|
||||||
border-radius: 2px;
|
|
||||||
border-right: 0;
|
|
||||||
border-top: 0;
|
|
||||||
content: " ";
|
|
||||||
display: block;
|
|
||||||
height: 0.625em;
|
|
||||||
margin-top: -0.4375em;
|
|
||||||
pointer-events: none;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
right: 15px;
|
|
||||||
transform: rotate(-45deg);
|
|
||||||
transform-origin: center;
|
|
||||||
width: 0.625em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.climate-zone {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg g {
|
|
||||||
filter: drop-shadow( 4px 4px 3px rgba(0, 0, 0, .7));
|
|
||||||
}
|
|
||||||
|
|
||||||
.climate-zone:hover {
|
|
||||||
filter: brightness(1.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
svg text {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
function closest (el: Element, predicate: (e: Element) => boolean) {
|
|
||||||
do if (predicate(el)) return el;
|
|
||||||
while (el = el && <Element>el.parentNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
// Load CSS
|
|
||||||
document.getElementsByTagName('head')[0].innerHTML += (<any>window)['app.css'];
|
|
||||||
|
|
||||||
// In order to be able to style SVG elements with CSS, and register events with javascript, we must use inline SVG (we can't use an img tag)
|
|
||||||
// For this purpose, the SVG file contents are embedded in a javascript file
|
|
||||||
document.getElementById('zones-map').innerHTML = (<any>window)['climate-zones-map.svg'];
|
|
||||||
|
|
||||||
document.querySelectorAll("[data-activate-modal]").forEach(elt => {
|
|
||||||
elt.addEventListener('click', e => {
|
|
||||||
document.getElementById(elt.getAttribute('data-activate-modal')).classList.toggle('is-active', true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelectorAll('.modal-close, .modal-card-head .delete').forEach(elt => {
|
|
||||||
elt.addEventListener('click', e => {
|
|
||||||
closest(elt, e => e.classList.contains('modal')).classList.toggle('is-active', false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
let zoneSelector = <HTMLSelectElement>document.getElementById('zone-selector');
|
|
||||||
document.querySelectorAll('.climate-zone').forEach(elt => {
|
|
||||||
elt.addEventListener('click', e => {
|
|
||||||
let zoneName = elt.getAttribute('id');
|
|
||||||
zoneSelector.value = zoneName;
|
|
||||||
closest(elt, e => e.classList.contains('modal')).classList.toggle('is-active', false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"module": "system",
|
|
||||||
"noImplicitAny": true,
|
|
||||||
"removeComments": true,
|
|
||||||
"preserveConstEnums": true,
|
|
||||||
"outFile": "../../.intermediate/app.js",
|
|
||||||
"sourceMap": false
|
|
||||||
},
|
|
||||||
"include": ["./**/*", "../../.intermediate/app/**/*"]
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
.modal-background {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-card-head {
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
border-top-right-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-card {
|
|
||||||
box-shadow: 4px 3px 10px 3px rgba(0,0,0,0.7);
|
|
||||||
}
|
|
77
simulator/src/simulator.scss
Normal file
77
simulator/src/simulator.scss
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#simulator {
|
||||||
|
@import "3rdparty/bulma/bulma.sass";
|
||||||
|
|
||||||
|
// properties that Bulma would set on the <html> tag
|
||||||
|
font-size: $body-size;
|
||||||
|
text-rendering: $body-rendering;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: $body-background-color;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
min-width: $body-min-width;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
text-size-adjust: 100%;
|
||||||
|
|
||||||
|
// properties that Bulma would set on the <body> tag
|
||||||
|
font-weight: $body-weight;
|
||||||
|
line-height: $body-line-height;
|
||||||
|
font-family: $body-family;
|
||||||
|
color: $body-color;
|
||||||
|
& > section, & > footer {
|
||||||
|
font-size: $body-font-size; // we must set this on each direct child of the #simulator div, because if it's specified in em, it's multiplied by the parent font-size
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=number] {
|
||||||
|
-moz-appearance:textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wide-label .field-label {
|
||||||
|
flex-grow: 2.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown.is-fullwidth {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown.is-fullwidth .dropdown-trigger,
|
||||||
|
.dropdown.is-fullwidth .dropdown-menu {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-trigger.with-dropdown-icon::after {
|
||||||
|
border: 3px solid black;
|
||||||
|
border-radius: 2px;
|
||||||
|
border-right: 0;
|
||||||
|
border-top: 0;
|
||||||
|
content: " ";
|
||||||
|
display: block;
|
||||||
|
height: 0.625em;
|
||||||
|
margin-top: -0.4375em;
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 15px;
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
transform-origin: center;
|
||||||
|
width: 0.625em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.climate-zone {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg g {
|
||||||
|
filter: drop-shadow( 4px 4px 3px rgba(0, 0, 0, .7));
|
||||||
|
}
|
||||||
|
|
||||||
|
.climate-zone:hover {
|
||||||
|
filter: brightness(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg text {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
@ -1,26 +1,36 @@
|
|||||||
|
function closest (el: Element, predicate: (e: Element) => boolean) {
|
||||||
|
do if (predicate(el)) return el;
|
||||||
|
while (el = el && <Element>el.parentNode);
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
let frame = <HTMLIFrameElement>document.querySelector('iframe#simulator');
|
let container = document.getElementById('simulator');
|
||||||
let doc = frame.contentWindow.document;
|
|
||||||
|
|
||||||
frame.style.width = '100%';
|
// Insert HTML code in the container
|
||||||
frame.style.border = 'none';
|
container.innerHTML += (<any>window)['simulator.html'];
|
||||||
frame.setAttribute('scrolling', 'no');
|
|
||||||
|
|
||||||
// Insert HTML code in the iframe
|
// In order to be able to style SVG elements with CSS, and register events with javascript, we must use inline SVG (we can't use an img tag)
|
||||||
doc.open();
|
// For this purpose, the SVG file contents are embedded in a javascript file
|
||||||
doc.write((<any>window)['simulator.html']);
|
document.getElementById('zones-map').innerHTML = (<any>window)['climate-zones-map.svg'];
|
||||||
doc.close();
|
|
||||||
|
|
||||||
// Load iframe specific CSS
|
document.querySelectorAll("[data-activate-modal]").forEach(elt => {
|
||||||
doc.head.innerHTML += (<any>window)['simulator-in-iframe.css'];
|
elt.addEventListener('click', e => {
|
||||||
|
document.getElementById(elt.getAttribute('data-activate-modal')).classList.toggle('is-active', true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Add script inside frame
|
document.querySelectorAll('.modal-close, .modal-card-head .delete').forEach(elt => {
|
||||||
let script = doc.createElement('script');
|
elt.addEventListener('click', e => {
|
||||||
script.type = "text/javascript";
|
closest(elt, e => e.classList.contains('modal')).classList.toggle('is-active', false);
|
||||||
script.innerText = (<any>window)['app.js'];
|
});
|
||||||
doc.body.appendChild(script);
|
});
|
||||||
|
|
||||||
setInterval(() => {
|
let zoneSelector = <HTMLSelectElement>document.getElementById('zone-selector');
|
||||||
frame.height = Math.max(doc.body.scrollHeight, 700) + 'px';
|
document.querySelectorAll('.climate-zone').forEach(elt => {
|
||||||
}, 100);
|
elt.addEventListener('click', e => {
|
||||||
|
let zoneName = elt.getAttribute('id');
|
||||||
|
zoneSelector.value = zoneName;
|
||||||
|
closest(elt, e => e.classList.contains('modal')).classList.toggle('is-active', false);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -7,5 +7,5 @@
|
|||||||
"outFile": "../www/simulator.js",
|
"outFile": "../www/simulator.js",
|
||||||
"sourceMap": true
|
"sourceMap": true
|
||||||
},
|
},
|
||||||
"include": ["./*", "../.intermediate/simulator.html.ts", "../.intermediate/simulator-in-iframe.css.ts", "../.intermediate/app.js.ts"]
|
"include": ["./**/*", "../.intermediate/**/*"]
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,9 @@ if (!fs.existsSync(intermediateDir+'/app')){
|
|||||||
}
|
}
|
||||||
|
|
||||||
let commands = [
|
let commands = [
|
||||||
|
() => child_process.fork(nodeModulesDir + '/node-sass/bin/node-sass', ['simulator.scss', '../.intermediate/simulator.css'], {'cwd': srcDir}),
|
||||||
() => child_process.fork(toolsDir + '/purify.js', {'cwd': srcDir}),
|
() => child_process.fork(toolsDir + '/purify.js', {'cwd': srcDir}),
|
||||||
() => child_process.fork(toolsDir + '/embed.js'),
|
() => child_process.fork(toolsDir + '/embed.js'),
|
||||||
() => child_process.fork(nodeModulesDir + '/typescript/lib/tsc.js', {'cwd': srcDir+'/app'}),
|
|
||||||
() => child_process.fork(toolsDir + '/embed-app.js'),
|
|
||||||
() => child_process.fork(nodeModulesDir + '/typescript/lib/tsc.js', {'cwd': srcDir})
|
() => child_process.fork(nodeModulesDir + '/typescript/lib/tsc.js', {'cwd': srcDir})
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
let fs = require('fs')
|
|
||||||
|
|
||||||
function embedJs(src, dst) {
|
|
||||||
fs.readFile(src, 'utf8', function(err, data) {
|
|
||||||
if(err) throw err;
|
|
||||||
|
|
||||||
data = data.replace(/\\"/g, '\\\\"');
|
|
||||||
data = "(<any>window)['"+src.replace(/^.*[\\\/]/, '')+"'] = `" + data + "`;";
|
|
||||||
|
|
||||||
fs.writeFile(dst, data, function(err) {
|
|
||||||
if(err) throw err;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let toolsDir = __dirname;
|
|
||||||
let dataDir = toolsDir + "/../data";
|
|
||||||
let srcDir = toolsDir + "/../src";
|
|
||||||
let intermediateDir = toolsDir + "/../.intermediate";
|
|
||||||
|
|
||||||
embedJs(intermediateDir+'/app.js', intermediateDir+'/app.js.ts');
|
|
@ -82,7 +82,5 @@ let srcDir = toolsDir + "/../src";
|
|||||||
let intermediateDir = toolsDir + "/../.intermediate";
|
let intermediateDir = toolsDir + "/../.intermediate";
|
||||||
|
|
||||||
embedHtml(srcDir+'/simulator.html', intermediateDir+'/simulator.html.ts');
|
embedHtml(srcDir+'/simulator.html', intermediateDir+'/simulator.html.ts');
|
||||||
embedCss(intermediateDir+'/app.css', intermediateDir+'/app/app.css.ts');
|
embedSvg(dataDir+'/climate-zones-map.svg', intermediateDir+'/climate-zones-map.svg.ts');
|
||||||
embedCss(srcDir+'/simulator-in-iframe.css', intermediateDir+'/simulator-in-iframe.css.ts');
|
embedCsv(dataDir+'/climate-zones-data.csv', intermediateDir+'/climate-zones-data.ts');
|
||||||
embedSvg(dataDir+'/climate-zones-map.svg', intermediateDir+'/app/climate-zones-map.svg.ts');
|
|
||||||
embedCsv(dataDir+'/climate-zones-data.csv', intermediateDir+'/app/climate-zones-data.ts');
|
|
||||||
|
@ -4,12 +4,12 @@ const purify = require("purify-css")
|
|||||||
let content = ['./*.html'];
|
let content = ['./*.html'];
|
||||||
|
|
||||||
// Reference of all CSS files from root directory
|
// Reference of all CSS files from root directory
|
||||||
let css = ['3rdparty/bulma/css/bulma.css', './app/app.css'];
|
let css = ['../.intermediate/simulator.css'];
|
||||||
|
|
||||||
let options = {
|
let options = {
|
||||||
output: '../.intermediate/app.css',
|
output: '../www/simulator.css',
|
||||||
whitelist: ['is-multiple', 'is-loading', 'is-narrow', 'is-active', 'climate-zone'],
|
whitelist: ['is-multiple', 'is-loading', 'is-narrow', 'is-active', 'climate-zone', 'is-max-desktop', 'is-max-widescreen'],
|
||||||
minify: true,
|
minify: false,
|
||||||
info: false
|
info: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,16 +3,20 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" href="simulator.css">
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
body { margin: 0; }
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Simulateur de vhélio</h1>
|
<h1>Simulateur de vhélio</h1>
|
||||||
<p>Estimez votre autonomie et votre consommation d'énergie</p>
|
<p>Estimez votre autonomie et votre consommation d'énergie</p>
|
||||||
|
|
||||||
<iframe id="simulator"></iframe>
|
<div id="simulator"></div>
|
||||||
|
|
||||||
<script type="text/javascript" src="simulator.js"></script>
|
<script type="text/javascript" src="simulator.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
Loading…
Reference in New Issue
Block a user