feeles-ide
Version:
The hackable and serializable IDE to make learning material
785 lines (659 loc) • 28.8 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _objectSpread3 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread"));
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf3 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireWildcard(require("react"));
var _styles = require("@material-ui/core/styles");
var _propTypes = _interopRequireDefault(require("prop-types"));
var _typestyle = require("typestyle");
var _eventemitter = _interopRequireDefault(require("eventemitter2"));
var _Snackbar = _interopRequireDefault(require("@material-ui/core/Snackbar"));
var _jsYaml = _interopRequireDefault(require("js-yaml"));
var _IconButton = _interopRequireDefault(require("@material-ui/core/IconButton"));
var _MenuItem = _interopRequireDefault(require("@material-ui/core/MenuItem"));
var _ListItemIcon = _interopRequireDefault(require("@material-ui/core/ListItemIcon"));
var _ListItemText = _interopRequireDefault(require("@material-ui/core/ListItemText"));
var _Drawer = _interopRequireDefault(require("@material-ui/core/Drawer"));
var _ArrowBack = _interopRequireDefault(require("@material-ui/icons/ArrowBack"));
var _CheckBox = _interopRequireDefault(require("@material-ui/icons/CheckBox"));
var _CheckBoxOutlineBlank = _interopRequireDefault(require("@material-ui/icons/CheckBoxOutlineBlank"));
var _convertAsset = _interopRequireDefault(require("../utils/convertAsset"));
var _database = require("../database/");
var _File = require("../File/");
var _codemirrorStyle = _interopRequireDefault(require("../js/codemirrorStyle"));
var MonitorTypes = _interopRequireWildcard(require("../utils/MonitorTypes"));
var _Menu = _interopRequireDefault(require("../Menu/"));
var _FileDialog = _interopRequireWildcard(require("../FileDialog/"));
var _CardContainer = _interopRequireDefault(require("../Cards/CardContainer"));
var _CloneDialog = _interopRequireDefault(require("../Menu/CloneDialog"));
var _icons = _interopRequireDefault(require("./icons"));
var _dec, _class, _class2, _temp;
var tryParseYAML = function tryParseYAML(text) {
var defaultValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
try {
return _jsYaml.default.safeLoad(text);
} catch (e) {
console.info(e);
return defaultValue;
}
};
var tryParseJSON = function tryParseJSON(text) {
var defaultValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
try {
return JSON.parse(text);
} catch (e) {
console.info(e);
return defaultValue;
}
};
var DOWNLOAD_ENABLED = typeof document.createElement('a').download === 'string';
var cn = {
root: (0, _typestyle.style)({
position: 'relative',
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
lineHeight: 1.15
}),
alignRight: (0, _typestyle.style)({
textAlign: 'right'
}),
drawer: {
paper: (0, _typestyle.style)({
position: 'absolute'
})
}
};
var Main = (_dec = (0, _styles.withTheme)(), _dec(_class = (_temp = _class2 =
/*#__PURE__*/
function (_Component) {
(0, _inherits2.default)(Main, _Component);
function Main() {
var _getPrototypeOf2;
var _this;
(0, _classCallCheck2.default)(this, Main);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = (0, _possibleConstructorReturn2.default)(this, (_getPrototypeOf2 = (0, _getPrototypeOf3.default)(Main)).call.apply(_getPrototypeOf2, [this].concat(args)));
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "state", {
openSidebar: false,
monitorType: MonitorTypes.Card,
fileView: new _database.FileView(_this.props.files),
reboot: false,
href: 'index.html',
project: _this.props.project,
notice: null,
// Advanced Mode
showAll: false,
// card =(emit)=> globalEvent =(on)=> card
globalEvent: new _eventemitter.default({
wildcard: true
}),
// Asset definition exactly using
asset: null,
isExpandingEditorCard: false // 集中執筆モード的な
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "_configs", new Map());
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "getConfig", function (key) {
if (_this._configs.has(key)) {
return _this._configs.get(key);
} else {
var _configs$get = _File.configs.get(key),
test = _configs$get.test,
defaultValue = _configs$get.defaultValue,
multiple = _configs$get.multiple,
bundle = _configs$get.bundle;
var files = _this.findFile(function (file) {
return !file.options.isTrashed && test.test(file.name);
}, multiple);
var value = files ? multiple ? bundle(files) : files.json : defaultValue;
_this._configs.set(key, value);
return value;
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "setConfig", function (key, config) {
_this._configs.set(key, config);
var _configs$get2 = _File.configs.get(key),
test = _configs$get2.test,
defaultName = _configs$get2.defaultName;
var configFile = _this.findFile(function (file) {
return !file.options.isTrashed && test.test(file.name);
}, false); // Update Mui theme
if (key === 'palette') {
var feelesrc = _this.loadConfig('feelesrc');
feelesrc.palette = config;
_this.props.setMuiTheme(feelesrc);
}
var indent = ' ';
var text = JSON.stringify(config, null, indent);
if (configFile) {
return _this.putFile(configFile, configFile.set({
text: text
}));
} else {
var newFile = new _File.SourceFile({
type: 'application/json',
name: defaultName,
text: text
});
return _this.addFile(newFile);
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "resetConfig", function (fileName) {
// Refresh config
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = _File.configs.entries()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _step$value = (0, _slicedToArray2.default)(_step.value, 2),
key = _step$value[0],
value = _step$value[1];
if (value.test.test(fileName)) {
_this._configs.delete(key);
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return != null) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "loadConfig", function (ext) {
var json = "".concat(ext, ".json");
var yaml = "".concat(ext, ".yml"); // TODO: オブジェクト(ハッシュ)以外も使えるようにする
var values = [].concat( // .json (JSON)
_this.state.fileView.getFilesByExtention(json).map(function (file) {
return tryParseJSON(file.text, {});
}), // .yml (YAML)
_this.state.fileView.getFilesByExtention(yaml).map(function (file) {
return tryParseYAML(file.text, {});
}), // .(ext) (JSON)
_this.state.fileView.getFilesByExtention(ext).map(function (file) {
return tryParseJSON(file.text, {});
}));
return Object.assign.apply(Object, [{}].concat((0, _toConsumableArray2.default)(values)));
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "saveAs", function () {
for (var _len2 = arguments.length, files = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
files[_key2] = arguments[_key2];
}
if (DOWNLOAD_ENABLED) {
files.forEach(function (file) {
var a = document.createElement('a');
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
a.download = file.name;
a.href = file.blobURL || URL.createObjectURL(new Blob([file.text], {
type: file.type
}));
a.dispatchEvent(event);
if (a.href !== file.blobURL) {
URL.revokeObjectURL(a.href);
}
});
return Promise.resolve();
} else {
// for Safari/IE11/Edge
return _this.openFileDialog(_FileDialog.SaveDialog, {
content: files
});
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "setProject", function (project) {
return _this.setStatePromise({
project: project
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleTogglePopout", function () {
var isPopout = _this.state.monitorType === MonitorTypes.Popout;
_this.setState({
reboot: true,
monitorType: isPopout ? MonitorTypes.Card : MonitorTypes.Popout
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleToggleFullScreen", function () {
var isFullScreen = _this.state.monitorType === MonitorTypes.FullScreen;
_this.setState({
monitorType: isFullScreen ? MonitorTypes.Card : MonitorTypes.FullScreen
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "setLocation", function (href) {
_this.setState(function (prevState) {
return {
reboot: true,
href: href || prevState.href
};
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleShowNotice", function (notice) {
return _this.setStatePromise({
notice: notice
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "toggleShowAll", function () {
return _this.setStatePromise({
showAll: !_this.state.showAll
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "openFileDialog", function () {
return console.info('openFileDialog has not be declared');
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleFileDialog", function (ref) {
return ref && (_this.openFileDialog = ref.open);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "setCardVisibility", function (name) {
var visible = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
_this.props.setCardProps(function (prevProps) {
var current = prevProps[name];
if (!current) {
throw TypeError("Property ".concat(name, " is not found in cardProps"));
}
return (0, _objectSpread3.default)({}, prevProps, (0, _defineProperty2.default)({}, name, (0, _objectSpread3.default)({}, current, {
visible: visible
})));
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "toggleSidebar", function () {
if (_this.props.mini) return; // mini の場合, stateless
_this.setState({
openSidebar: !_this.state.openSidebar
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "renderMenuItem", function (item, index) {
var localization = _this.props.localization;
var lowerCase = item.name.substr(0, 1).toLowerCase() + item.name.substr(1);
var localized = localization[lowerCase];
var visible = _this.props.cardProps[item.name].visible;
return _react.default.createElement(_MenuItem.default, {
key: index,
onClick: function onClick() {
_this.setCardVisibility(item.name, !visible);
}
}, _react.default.createElement(_ListItemIcon.default, null, item.icon), _react.default.createElement(_ListItemText.default, null, localized ? localized.title : item.name), _react.default.createElement(_ListItemIcon.default, null, visible ? _react.default.createElement(_CheckBox.default, null) : _react.default.createElement(_CheckBoxOutlineBlank.default, null)));
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "setExpandingEditorCard", function (isExpandingEditorCard) {
return _this.setState({
isExpandingEditorCard: isExpandingEditorCard
});
});
return _this;
}
(0, _createClass2.default)(Main, [{
key: "componentWillMountCompat",
/**
* 元々 componentWillMount で実装されていた処理.
* render が最初に呼ばれたときに一度だけ呼ばれる
*/
value: function componentWillMountCompat() {
// 互換性保持のため、 fileView に外から setState させる
this.state.fileView.install(this);
}
}, {
key: "componentDidMount",
value: function componentDidMount() {
var _this2 = this;
this.updateAssetDefinition(); // 定期的にスクリーンショットを撮る
if (this.props.onThumbnailChange) {
var globalEvent = this.state.globalEvent;
var cache = new Set();
globalEvent.on('message.capture', function (event) {
var _ref = event.data || {},
value = _ref.value;
if (!cache.has(value)) {
_this2.props.onThumbnailChange(value);
cache.add(value);
}
});
var screenShotLater =
/*#__PURE__*/
function () {
var _ref2 = (0, _asyncToGenerator2.default)(
/*#__PURE__*/
_regenerator.default.mark(function _callee() {
var request;
return _regenerator.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return new Promise(function (resolve) {
return window.setTimeout(resolve, 10 * 1000);
});
case 2:
request = {
query: 'capture',
type: 'image/jpeg',
requestedBy: 'auto' // 自動的にリクエストされることを表す
};
_context.next = 5;
return _this2.state.globalEvent.emitAsync('postMessage', request);
case 5:
screenShotLater();
case 6:
case "end":
return _context.stop();
}
}
}, _callee, this);
}));
return function screenShotLater() {
return _ref2.apply(this, arguments);
};
}();
screenShotLater();
}
if (this.props.onChange) {
// ファイルの内容を伝える(一番最初)
this.props.onChange({
files: this.props.files
});
} // iframe からのイベントを伝える
this.state.globalEvent.on('message.dispatchOnMessage', function (event) {
if (_this2.props.onMessage) {
_this2.props.onMessage((0, _objectSpread3.default)({}, event));
}
});
var feelesrc = this.loadConfig('feelesrc');
this.props.setMuiTheme(feelesrc);
}
}, {
key: "componentDidUpdate",
value: function () {
var _componentDidUpdate = (0, _asyncToGenerator2.default)(
/*#__PURE__*/
_regenerator.default.mark(function _callee2(prevProps, prevState) {
var _this3 = this;
var _this$props, localization, project, _project;
return _regenerator.default.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
// mini の場合, openSidebar の props に追従 (stateless)
if (this.props.mini && this.props.openSidebar !== this.state.openSidebar) {
this.setState({
openSidebar: this.props.openSidebar
});
}
_this$props = this.props, localization = _this$props.localization, project = _this$props.project;
if (prevProps.project !== project) {
this.setProject(project);
}
if (prevProps.localization !== localization) {
this.state.fileView.forceUpdate();
this.setState({
reboot: true
});
}
if (this.state.reboot) {
this.setState({
reboot: false
});
} // ファイル変更検知
if (this.props.onChange && prevState.fileView !== this.state.fileView) {
this.props.onChange({
files: this.state.fileView.files
});
} // 未オートセーブでファイルが更新されたとき、あらたにセーブデータを作る
if (!(!this.state.project && prevState.fileView !== this.state.fileView)) {
_context2.next = 24;
break;
}
if (!(process.env.NODE_ENV !== 'production')) {
_context2.next = 9;
break;
}
return _context2.abrupt("return");
case 9:
if (!this.props.disableLocalSave) {
_context2.next = 11;
break;
}
return _context2.abrupt("return");
case 11:
_context2.prev = 11;
_context2.next = 14;
return (0, _database.createProject)(this.state.fileView.files.map(function (item) {
return item.serialize();
}));
case 14:
_project = _context2.sent;
_context2.next = 17;
return this.setProject(_project);
case 17:
_context2.next = 23;
break;
case 19:
_context2.prev = 19;
_context2.t0 = _context2["catch"](11);
console.log(_context2.t0);
if (typeof _context2.t0 === 'string' && _context2.t0 in localization.cloneDialog) {
alert(localization.cloneDialog[_context2.t0]);
}
case 23:
// notice
this.setState({
notice: {
message: localization.cloneDialog.autoSaved,
action: localization.cloneDialog.setTitle,
autoHideDuration: 20000,
onActionClick: function onActionClick() {
_this3.openFileDialog(_CloneDialog.default, {
files: _this3.state.fileView.files,
project: _this3.state.project,
setProject: _this3.setProject,
launchIDE: _this3.props.launchIDE
});
}
}
});
case 24:
case "end":
return _context2.stop();
}
}
}, _callee2, this, [[11, 19]]);
}));
function componentDidUpdate(_x, _x2) {
return _componentDidUpdate.apply(this, arguments);
}
return componentDidUpdate;
}()
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
this.state.fileView.uninstall();
}
}, {
key: "setStatePromise",
value: function () {
var _setStatePromise = (0, _asyncToGenerator2.default)(
/*#__PURE__*/
_regenerator.default.mark(function _callee3(state) {
var _this4 = this;
return _regenerator.default.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
return _context3.abrupt("return", new Promise(function (resolve) {
_this4.setState(state, resolve);
}));
case 1:
case "end":
return _context3.stop();
}
}
}, _callee3, this);
}));
function setStatePromise(_x3) {
return _setStatePromise.apply(this, arguments);
}
return setStatePromise;
}()
}, {
key: "updateAssetDefinition",
/**
* 現行: アセット定義を setState する
* 互換: 旧アセット定義を変換して使用する
*/
value: function updateAssetDefinition() {
var asset = this.props.asset;
if (asset) {
if (asset !== this.state.asset) {
this.setState({
asset: asset
});
}
return;
} // 互換モード
var configs = this.state.fileView.getFilesByExtention('asset.yml').map(function (file) {
return file.text + '';
});
var compatibleAsset = (0, _convertAsset.default)(configs);
this.setState({
asset: compatibleAsset
});
}
}, {
key: "render",
value: function render() {
var _this5 = this;
if (this.componentWillMountCompat) {
// render よりも先に呼ばれるライフサイクルメソッドがないので,
// ここで呼んでいる. 一度しか呼ばれないように参照を null にする
this.componentWillMountCompat();
this.componentWillMountCompat = null;
return null;
}
var _this$props2 = this.props,
localization = _this$props2.localization,
mini = _this$props2.mini;
var commonProps = {
fileView: this.state.fileView,
files: this.state.fileView.files,
localization: localization,
getConfig: this.getConfig,
setConfig: this.setConfig,
loadConfig: this.loadConfig,
findFile: this.findFile,
addFile: this.addFile,
putFile: this.putFile,
showAll: this.state.showAll
};
var userStyle = this.findFile('feeles/codemirror.css');
return _react.default.createElement("div", {
className: cn.root
}, mini ? null : _react.default.createElement(_Menu.default, (0, _extends2.default)({}, commonProps, {
cardProps: this.props.cardProps,
toggleSidebar: this.toggleSidebar,
setLocalization: this.props.setLocalization,
openFileDialog: this.openFileDialog,
saveAs: this.saveAs,
project: this.state.project,
setCardVisibility: this.setCardVisibility,
showAll: this.state.showAll,
toggleShowAll: this.toggleShowAll,
globalEvent: this.state.globalEvent
})), _react.default.createElement(_Drawer.default, {
variant: "persistent",
open: this.state.openSidebar,
onClose: this.toggleSidebar,
classes: cn.drawer
}, this.props.mini ? null : _react.default.createElement("div", {
className: cn.alignRight
}, _react.default.createElement(_IconButton.default, {
onClick: this.toggleSidebar
}, _react.default.createElement(_ArrowBack.default, null))), _icons.default.map(this.renderMenuItem)), _react.default.createElement(_CardContainer.default, (0, _extends2.default)({}, commonProps, {
cardProps: this.props.cardProps,
setCardVisibility: this.setCardVisibility,
setLocation: this.setLocation,
openFileDialog: this.openFileDialog,
reboot: this.state.reboot,
href: this.state.href,
monitorType: this.state.monitorType,
saveAs: this.saveAs,
toggleFullScreen: this.handleToggleFullScreen,
togglePopout: this.handleTogglePopout,
showNotice: this.handleShowNotice,
deleteFile: this.deleteFile,
ref: this.handleContainerRef,
globalEvent: this.state.globalEvent,
asset: this.state.asset,
isExpandingEditorCard: this.state.isExpandingEditorCard,
setExpandingEditorCard: this.setExpandingEditorCard
})), _react.default.createElement(_FileDialog.default, {
ref: this.handleFileDialog,
localization: this.props.localization,
getConfig: this.getConfig,
setConfig: this.setConfig,
globalEvent: this.state.globalEvent
}), _react.default.createElement("style", null, (0, _codemirrorStyle.default)(this.props.theme)), userStyle ? _react.default.createElement("style", null, userStyle.text) : null, _react.default.createElement(_Snackbar.default, (0, _extends2.default)({
open: this.state.notice !== null,
message: "",
autoHideDuration: 4000,
onClose: function onClose() {
return _this5.setState({
notice: null
});
}
}, this.state.notice)));
}
}, {
key: "rootWidth",
get: function get() {
return parseInt(this.props.rootStyle.width, 10);
}
}, {
key: "rootHeight",
get: function get() {
return parseInt(this.props.rootStyle.height, 10);
}
}]);
return Main;
}(_react.Component), (0, _defineProperty2.default)(_class2, "propTypes", {
theme: _propTypes.default.object.isRequired,
cardProps: _propTypes.default.object.isRequired,
setCardProps: _propTypes.default.func.isRequired,
openSidebar: _propTypes.default.bool.isRequired,
mini: _propTypes.default.bool.isRequired,
files: _propTypes.default.array.isRequired,
rootStyle: _propTypes.default.object.isRequired,
project: _propTypes.default.object,
launchIDE: _propTypes.default.func.isRequired,
localization: _propTypes.default.object.isRequired,
setLocalization: _propTypes.default.func.isRequired,
setMuiTheme: _propTypes.default.func.isRequired,
onChange: _propTypes.default.func,
onMessage: _propTypes.default.func,
onThumbnailChange: _propTypes.default.func,
disableLocalSave: _propTypes.default.bool.isRequired,
asset: _propTypes.default.object
}), _temp)) || _class);
exports.default = Main;