UNPKG

feeles-ide

Version:

The hackable and serializable IDE to make learning material

785 lines (659 loc) 28.8 kB
"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;