1
0
mirror of https://github.com/chinchang/web-maker.git synced 2025-07-29 09:40:10 +02:00

port saveAsHtml

This commit is contained in:
Kushagra Gour
2018-06-07 11:05:59 +05:30
parent d3ba0534aa
commit 6794e309af
4 changed files with 373 additions and 350 deletions

View File

@@ -2,7 +2,7 @@ import { h, Component } from 'preact';
import UserCodeMirror from './UserCodeMirror.jsx';
import { computeHtml, computeCss, computeJs } from '../computes';
import { modes, HtmlModes, CssModes, JsModes } from '../codeModes';
import { log, writeFile, loadJS } from '../utils';
import { log, writeFile, loadJS, getCompleteHtml } from '../utils';
import { SplitPane } from './SplitPane.jsx';
import { trackEvent } from '../analytics';
import CodeMirror from '../CodeMirror';
@@ -76,78 +76,15 @@ export default class ContentWrap extends Component {
}
clearConsole() {}
/* eslint max-params: ["error", 4] */
getCompleteHtml(html, css, js, isForExport) {
if (!this.props.currentItem) {
return '';
}
var externalJs = this.props.currentItem.externalLibs.js
.split('\n')
.reduce(function(scripts, url) {
return scripts + (url ? '\n<script src="' + url + '"></script>' : '');
}, '');
var externalCss = this.props.currentItem.externalLibs.css
.split('\n')
.reduce(function(links, url) {
return (
links +
(url ? '\n<link rel="stylesheet" href="' + url + '"></link>' : '')
);
}, '');
var contents =
'<!DOCTYPE html>\n' +
'<html>\n<head>\n' +
'<meta charset="UTF-8" />\n' +
externalCss +
'\n' +
'<style id="webmakerstyle">\n' +
css +
'\n</style>\n' +
'</head>\n' +
'<body>\n' +
html +
'\n' +
externalJs +
'\n';
if (!isForExport) {
contents +=
'<script src="' +
(chrome.extension
? chrome.extension.getURL('lib/screenlog.js')
: `${location.origin}${BASE_PATH}/lib/screenlog.js`) +
'"></script>';
}
if (this.jsMode === JsModes.ES6) {
contents +=
'<script src="' +
(chrome.extension
? chrome.extension.getURL('lib/transpilers/babel-polyfill.min.js')
: `${
location.origin
}${BASE_PATH}/lib/transpilers/babel-polyfill.min.js`) +
'"></script>';
}
if (typeof js === 'string') {
contents += '<script>\n' + js + '\n//# sourceURL=userscript.js';
} else {
var origin = chrome.i18n.getMessage()
? `chrome-extension://${chrome.i18n.getMessage('@@extension_id')}`
: `${location.origin}`;
contents +=
'<script src="' + `filesystem:${origin}/temporary/script.js` + '">';
}
contents += '\n</script>\n</body>\n</html>';
return contents;
}
createPreviewFile(html, css, js) {
const shouldInlineJs =
!window.webkitRequestFileSystem || !window.IS_EXTENSION;
var contents = this.getCompleteHtml(html, css, shouldInlineJs ? js : null);
var contents = getCompleteHtml(
html,
css,
shouldInlineJs ? js : null,
this.props.currentItem
);
var blob = new Blob([contents], { type: 'text/plain;charset=UTF-8' });
var blobjs = new Blob([js], { type: 'text/plain;charset=UTF-8' });

View File

@@ -17,6 +17,7 @@ export default class Footer extends Component {
<div id="footer" class="footer">
<div class="footer__right fr">
<a
onClick={this.props.saveHtmlBtnClickHandler}
id="saveHtmlBtn"
class="mode-btn hint--rounded hint--top-left hide-on-mobile"
data-hint="Save as HTML file"

View File

@@ -9,7 +9,7 @@ import AddLibrary from './AddLibrary.jsx';
import Modal from './Modal.jsx';
import HelpModal from './HelpModal.jsx';
import Login from './Login.jsx';
import { log, generateRandomId, semverCompare } from '../utils';
import { log, generateRandomId, semverCompare, saveAsHtml } from '../utils';
import { itemService } from '../itemService';
import '../db';
import Notifications from './Notifications';
@@ -446,40 +446,6 @@ export default class App extends Component {
});
}
saveFile() {
var htmlPromise = computeHtml();
var cssPromise = computeCss();
var jsPromise = computeJs(false);
Promise.all([htmlPromise, cssPromise, jsPromise]).then(function(result) {
var html = result[0],
css = result[1],
js = result[2];
var fileContent = getCompleteHtml(html, css, js, true);
var d = new Date();
var fileName = [
'web-maker',
d.getFullYear(),
d.getMonth() + 1,
d.getDate(),
d.getHours(),
d.getMinutes(),
d.getSeconds()
].join('-');
if (this.state.currentItem.title) {
fileName = this.state.currentItem.title;
}
fileName += '.html';
var blob = new Blob([fileContent], { type: 'text/html;charset=UTF-8' });
utils.downloadFile(fileName, blob);
trackEvent('fn', 'saveFileComplete');
});
}
closeAllOverlays() {
if (this.state.isSavedItemPaneOpen) {
this.setState({ isSavedItemPaneOpen: false });
@@ -833,6 +799,11 @@ export default class App extends Component {
trackEvent('ui', 'openInCodepen');
e.preventDefault();
}
saveHtmlBtnClickHandler(e) {
saveAsHtml(this.state.currentItem);
trackEvent('ui', 'saveHtmlClick');
e.preventDefault();
}
render() {
return (
@@ -880,6 +851,7 @@ export default class App extends Component {
this
)}
codepenBtnClickHandler={this.codepenBtnClickHandler.bind(this)}
saveHtmlBtnClickHandler={this.saveHtmlBtnClickHandler.bind(this)}
hasUnseenChangelog={this.state.hasUnseenChangelog}
/>
</div>

View File

@@ -1,18 +1,27 @@
import {
import {
trackEvent
} from './analytics';
} from './analytics';
import {
import {
computeHtml,
computeCss,
computeJs
} from './computes';
import {
JsModes
} from './codeModes';
import {
deferred
} from './deferred';
window.DEBUG = document.cookie.indexOf('wmdebug') > -1;
} from './deferred';
window.$ = document.querySelector.bind(document);
window.$all = selector => [...document.querySelectorAll(selector)];
var alphaNum =
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
window.DEBUG = document.cookie.indexOf('wmdebug') > -1;
window.$ = document.querySelector.bind(document);
window.$all = selector => [...document.querySelectorAll(selector)];
const BASE_PATH = chrome.extension || window.DEBUG ? '/' : '/app';
/**
var alphaNum = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
/**
* The following 2 functions are supposed to find the next/previous sibling until the
* passed `selector` is matched. But for now it actually finds the next/previous
* element of `this` element in the list of `selector` matched element inside `this`'s
@@ -20,31 +29,31 @@
* @param Selector that should match for next siblings
* @return element Next element that mathes `selector`
*/
Node.prototype.nextUntil = function (selector) {
Node.prototype.nextUntil = function (selector) {
const siblings = [...this.parentNode.querySelectorAll(selector)];
const index = siblings.indexOf(this);
return siblings[index + 1];
};
};
// Safari doesn't have this!
window.requestIdleCallback =
// Safari doesn't have this!
window.requestIdleCallback =
window.requestIdleCallback ||
function (fn) {
setTimeout(fn, 10);
};
/*
/*
* @param Selector that should match for next siblings
* @return element Next element that mathes `selector`
*/
Node.prototype.previousUntil = function (selector) {
Node.prototype.previousUntil = function (selector) {
const siblings = [...this.parentNode.querySelectorAll(selector)];
const index = siblings.indexOf(this);
return siblings[index - 1];
};
};
// https://github.com/substack/semver-compare/blob/master/index.js
export function semverCompare(a, b) {
// https://github.com/substack/semver-compare/blob/master/index.js
export function semverCompare(a, b) {
var pa = a.split('.');
var pb = b.split('.');
for (var i = 0; i < 3; i++) {
@@ -64,51 +73,51 @@
}
}
return 0;
}
}
export function generateRandomId(len) {
export function generateRandomId(len) {
var length = len || 10;
var id = '';
for (var i = length; i--;) {
id += alphaNum[~~(Math.random() * alphaNum.length)];
}
return id;
}
}
export function onButtonClick(btn, listener) {
export function onButtonClick(btn, listener) {
btn.addEventListener('click', function buttonClickListener(e) {
listener(e);
return false;
});
}
}
export function log() {
export function log() {
if (window.DEBUG) {
console.log(Date.now(), ...arguments);
}
}
}
/**
/**
* Adds timed limit on the loops found in the passed code.
* Contributed by Ariya Hidayat!
* @param code {string} Code to be protected from infinite loops.
*/
export function addInfiniteLoopProtection(code, {
export function addInfiniteLoopProtection(code, {
timeout
}) {
}) {
var loopId = 1;
var patches = [];
var varPrefix = '_wmloopvar';
var varStr = 'var %d = Date.now();\n';
var checkStr = `\nif (Date.now() - %d > ${timeout}) { window.top.previewException(new Error("Infinite loop")); break;}\n`;
esprima.parse(code, {
esprima.parse(
code, {
tolerant: true,
range: true,
jsx: true
}, function (
node
) {
},
function (node) {
switch (node.type) {
case 'DoWhileStatement':
case 'ForStatement':
@@ -145,7 +154,8 @@
default:
break;
}
});
}
);
/* eslint-disable no-param-reassign */
patches
@@ -158,9 +168,9 @@
/* eslint-disable no-param-reassign */
return code;
}
}
export function getHumanDate(timestamp) {
export function getHumanDate(timestamp) {
var d = new Date(timestamp);
var retVal =
d.getDate() +
@@ -181,10 +191,10 @@
' ' +
d.getFullYear();
return retVal;
}
}
// create a one-time event
export function once(node, type, callback) {
// create a one-time event
export function once(node, type, callback) {
// create event
node.addEventListener(type, function (e) {
// remove event
@@ -192,9 +202,9 @@
// call handler
return callback(e);
});
}
}
export function downloadFile(fileName, blob) {
export function downloadFile(fileName, blob) {
function downloadWithAnchor() {
var a = document.createElement('a');
a.href = window.URL.createObjectURL(blob);
@@ -220,9 +230,9 @@
} else {
downloadWithAnchor();
}
}
}
export function writeFile(name, blob, cb) {
export function writeFile(name, blob, cb) {
var fileWritten = false;
function getErrorHandler(type) {
@@ -275,9 +285,9 @@
},
getErrorHandler('webkitRequestFileSystemFail')
);
}
}
export function loadJS(src) {
export function loadJS(src) {
var d = deferred();
var ref = window.document.getElementsByTagName('script')[0];
var script = window.document.createElement('script');
@@ -288,16 +298,119 @@
d.resolve();
};
return d.promise;
};
}
window.chrome = window.chrome || {};
window.chrome.i18n = {
getMessage: () => {}
};
window.IS_EXTENSION = !!window.chrome.extension;
if (window.IS_EXTENSION) {
document.body.classList.add('is-extension');
} else {
document.body.classList.add('is-app');
export function getCompleteHtml(html, css, js, item, isForExport) {
if (!item) {
return '';
}
var externalJs = item.externalLibs.js
.split('\n')
.reduce(function (scripts, url) {
return scripts + (url ? '\n<script src="' + url + '"></script>' : '');
}, '');
var externalCss = item.externalLibs.css
.split('\n')
.reduce(function (links, url) {
return (
links +
(url ? '\n<link rel="stylesheet" href="' + url + '"></link>' : '')
);
}, '');
var contents =
'<!DOCTYPE html>\n' +
'<html>\n<head>\n' +
'<meta charset="UTF-8" />\n' +
externalCss +
'\n' +
'<style id="webmakerstyle">\n' +
css +
'\n</style>\n' +
'</head>\n' +
'<body>\n' +
html +
'\n' +
externalJs +
'\n';
if (!isForExport) {
contents +=
'<script src="' +
(chrome.extension ?
chrome.extension.getURL('lib/screenlog.js') :
`${location.origin}${BASE_PATH}/lib/screenlog.js`) +
'"></script>';
}
if (item.jsMode === JsModes.ES6) {
contents +=
'<script src="' +
(chrome.extension ?
chrome.extension.getURL('lib/transpilers/babel-polyfill.min.js') :
`${
location.origin
}${BASE_PATH}/lib/transpilers/babel-polyfill.min.js`) +
'"></script>';
}
if (typeof js === 'string') {
contents += '<script>\n' + js + '\n//# sourceURL=userscript.js';
} else {
var origin = chrome.i18n.getMessage() ?
`chrome-extension://${chrome.i18n.getMessage('@@extension_id')}` :
`${location.origin}`;
contents +=
'<script src="' + `filesystem:${origin}/temporary/script.js` + '">';
}
contents += '\n</script>\n</body>\n</html>';
return contents;
}
export function saveAsHtml(item) {
var htmlPromise = computeHtml(item.html, item.htmlMode);
var cssPromise = computeCss(item.css, item.cssMode);
var jsPromise = computeJs(item.js, item.jsMode, false);
Promise.all([htmlPromise, cssPromise, jsPromise]).then(function (result) {
var html = result[0],
css = result[1],
js = result[2];
var fileContent = getCompleteHtml(html, css, js, item, true);
var d = new Date();
var fileName = [
'web-maker',
d.getFullYear(),
d.getMonth() + 1,
d.getDate(),
d.getHours(),
d.getMinutes(),
d.getSeconds()
].join('-');
if (item.title) {
fileName = item.title;
}
fileName += '.html';
var blob = new Blob([fileContent], {
type: 'text/html;charset=UTF-8'
});
downloadFile(fileName, blob);
trackEvent('fn', 'saveFileComplete');
});
}
window.chrome = window.chrome || {};
window.chrome.i18n = {
getMessage: () => {}
};
window.IS_EXTENSION = !!window.chrome.extension;
if (window.IS_EXTENSION) {
document.body.classList.add('is-extension');
} else {
document.body.classList.add('is-app');
}