UNPKG

feeles-ide

Version:

The hackable and serializable IDE to make learning material

436 lines (371 loc) 13.5 kB
import _toConsumableArray from 'babel-runtime/helpers/toConsumableArray'; import _regeneratorRuntime from 'babel-runtime/regenerator'; import _asyncToGenerator from 'babel-runtime/helpers/asyncToGenerator'; import _getIterator from 'babel-runtime/core-js/get-iterator'; import _toArray from 'babel-runtime/helpers/toArray'; import _Map from 'babel-runtime/core-js/map'; import _classCallCheck from 'babel-runtime/helpers/classCallCheck'; import _createClass from 'babel-runtime/helpers/createClass'; import includes from 'lodash/includes'; import { putFile as _putFile, deleteFile as _deleteFile } from './'; /** * ファイルの状態を In Memory & IndexedDB で保有するストア * ファイルの探索を手助けする Indexer でもある * Redux は使わず、 FileView インスタンスが setState の * 参照をもち、外から状態を更新する */ var FileView = function () { function FileView(files) { _classCallCheck(this, FileView); this.files = files; this._changed = true; // files 変更フラグ } _createClass(FileView, [{ key: 'install', value: function install(component) { this.component = component; /* 互換性保持のため, つねに最新のステートを参照するAPIをのこす いずれ this.state.fileView を直接 propergate させたい */ component.addFile = this.addFile.bind(this); component.putFile = this.putFile.bind(this); component.deleteFile = this.deleteFile.bind(this); component.findFile = this.findFile.bind(this); } }, { key: 'uninstall', value: function uninstall() { this.component = null; } }, { key: 'setState', value: function setState(_ref) { var files = _ref.files; this._changed = true; // Index するフラグ if (!files) throw 'Cannot set other than files'; if (!this.component) throw 'Component is not been set'; var fileView = new FileView(files); fileView.install(this.component); return this.component.setStatePromise({ fileView: fileView }); } /** * ファイルパスでファイルを取得 * @param {String} path 取得したいファイルのパス * @return {SourceFile|BinalyFile|null} ファイルまたは null */ }, { key: 'getFileByFullPath', value: function getFileByFullPath(path) { this.updateIndex(); return this.pathToFileMap.get(path); } /** * 任意の拡張子をもつすべてのファイルを取得 * @param {String} ext 取得したいファイルのパス * @return {Array<SourceFile|BinalyFile>} ファイルの配列 */ }, { key: 'getFilesByExtention', value: function getFilesByExtention(ext) { this.updateIndex(); return this.extToFilesMap.get(ext) || []; } /** * Map を用いてファイル名をインデックス * files が変わったら適宜呼び出す */ }, { key: 'updateIndex', value: function updateIndex() { var _this = this; // files が変更されていればあらたにインデックス if (!this._changed) return; this._changed = false; this.pathToFileMap = new _Map(); this.extToFilesMap = new _Map(); var append = function append(name, file) { // name => File _this.pathToFileMap.set(name, file); // extention => File var _name$split = name.split('.'), _name$split2 = _toArray(_name$split), extArray = _name$split2.slice(1); if (!extArray.length) return; var ext = extArray.join('.'); if (_this.extToFilesMap.has(ext)) { var files = _this.extToFilesMap.get(ext); _this.extToFilesMap.set(ext, files.concat(file)); } else { _this.extToFilesMap.set(ext, [file]); } }; // i18n 以外 var i18n = []; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = _getIterator(this.files), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var file = _step.value; if (!file.name.startsWith('i18n/')) { append(file.name, file); } else { i18n.push(file); } } // i18n/{ll_CC} を追加 } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } var ll_CC = this.component.props.localization.ll_CC; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = _getIterator(i18n), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var _file = _step2.value; var _file$name$split = _file.name.split('/'), _file$name$split2 = _toArray(_file$name$split), locale = _file$name$split2[1], virtualPath = _file$name$split2.slice(2); if (locale === ll_CC) { append(virtualPath.join('/'), _file); } } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } } }, { key: 'forceUpdate', value: function forceUpdate() { var fileView = new FileView(this.files); fileView.install(this.component); return this.component.setStatePromise({ fileView: fileView }); } /** * ファイルを検索して取得する (後方互換性) * @param {String|Function} name ファイル名 * @param {Boolean} multiple 全件取得フラグ */ }, { key: 'findFile', value: function findFile(name) { var multiple = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (typeof name === 'string') { name = name.replace(/^(\.\/|\/)*/, ''); } var i18nName = 'i18n/' + this.component.props.localization.ll_CC + '/' + name; var pred = typeof name === 'function' ? name : function (file) { return !file.options.isTrashed && ( // 言語設定による動的ファイルパス解決 file.name === i18nName || file.moduleName === i18nName || // 通常のファイルパス解決 file.name === name || file.moduleName === name); }; return multiple ? this.files.filter(pred) : this.files.find(pred) || null; } /** * ファイルをひとつ追加する * @param {SourceFile|BinalyFile} file 追加するファイル */ }, { key: 'addFile', value: function () { var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(file) { var remove, files; return _regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: remove = this.inspection(file); if (!(file === remove)) { _context.next = 3; break; } return _context.abrupt('return', file); case 3: files = this.files.concat(file).filter(function (item) { return item !== remove; }); _context.next = 6; return this.setState({ files: files }); case 6: _context.next = 8; return this.component.resetConfig(file.name); case 8: if (!this.component.state.project) { _context.next = 11; break; } _context.next = 11; return _putFile(this.component.state.project.id, file.serialize()); case 11: return _context.abrupt('return', file); case 12: case 'end': return _context.stop(); } } }, _callee, this); })); function addFile(_x2) { return _ref2.apply(this, arguments); } return addFile; }() /** * ファイルを置き換える * @param {SourceFile|BinalyFile} prevFile 削除するファイル * @param {SourceFile|BinalyFile} nextFile 追加するファイル */ }, { key: 'putFile', value: function () { var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(prevFile, nextFile) { var remove, files; return _regeneratorRuntime.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: remove = this.inspection(nextFile); if (!(remove === nextFile)) { _context2.next = 3; break; } return _context2.abrupt('return', prevFile); case 3: files = this.files.filter(function (item) { return item !== remove && item.key !== prevFile.key; }).concat(nextFile); _context2.next = 6; return this.setState({ files: files }); case 6: this.component.resetConfig(prevFile.name); if (!this.component.state.project) { _context2.next = 10; break; } _context2.next = 10; return _putFile(this.component.state.project.id, nextFile.serialize()); case 10: return _context2.abrupt('return', nextFile); case 11: case 'end': return _context2.stop(); } } }, _callee2, this); })); function putFile(_x3, _x4) { return _ref3.apply(this, arguments); } return putFile; }() /** * 任意個数のファイルを削除する * @param {Array<SourceFile|BinalyFile>} targets 削除するファイル */ }, { key: 'deleteFile', value: function () { var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() { for (var _len = arguments.length, targets = Array(_len), _key = 0; _key < _len; _key++) { targets[_key] = arguments[_key]; } var keys, files, fileNames; return _regeneratorRuntime.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: keys = targets.map(function (item) { return item.key; }); files = this.files.filter(function (item) { return !includes(keys, item.key); }); _context3.next = 4; return this.setState({ files: files }); case 4: if (!this.component.state.project) { _context3.next = 8; break; } fileNames = targets.map(function (item) { return item.name; }); _context3.next = 8; return _deleteFile.apply(undefined, [this.component.state.project.id].concat(_toConsumableArray(fileNames))); case 8: case 'end': return _context3.stop(); } } }, _callee3, this); })); function deleteFile() { return _ref4.apply(this, arguments); } return deleteFile; }() /** * ファイル名の衝突をしらべる. TODO: FileDialog で実現すべき * https://trello.com/c/Y4CbIH81/244-conflict-%E3%81%AF%E3%83%80%E3%82%A4%E3%82%A2%E3%83%AD%E3%82%B0%E3%81%AE%E3%81%A8%E3%81%93%E3%82%8D%E3%81%A7%E5%88%A4%E5%AE%9A%E3%81%99%E3%82%8B-filestore-%E3%81%A7%E3%81%AF%E7%84%A1%E8%A6%96 * @param {Array<SourceFile|BinalyFile>} newFile 追加予定のファイル */ }, { key: 'inspection', value: function inspection(newFile) { var conflict = this.files.find(function (file) { return !file.options.isTrashed && file.key !== newFile.key && file.name === newFile.name; }); if (conflict) { // TODO: FileDialog instead of. // 一時的に "強制上書き" にする console.log(newFile); return conflict; // if (confirm(this.component.props.localization.common.conflict)) { // return conflict; // } else { // return newFile; // } } return null; } }]); return FileView; }(); export default FileView;