From 59b5dbe70d1be5e7efbae4a4a244307819754433 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 11 Apr 2024 17:53:32 +0200 Subject: [PATCH] wip: make the app integrate with the viewer app Signed-off-by: Robin Appelman --- .gitignore | 2 +- js/ImportAce.js | 25 - js/SyntaxMode.js | 178 - js/editor.js | 603 - js/index.js | 28 - js/newfileplugin.js | 40 - js/public-share.js | 109 - js/sidebarpreview.js | 81 - js/supported_mimetypes.json | 13 - lib/Listeners/LoadFilesScriptsListener.php | 3 +- package-lock.json | 26343 ++++++++++++++++--- package.json | 44 +- src/TextEditor.vue | 99 + src/main.js | 24 + webpack.config.js | 52 +- 15 files changed, 22319 insertions(+), 5325 deletions(-) delete mode 100644 js/ImportAce.js delete mode 100644 js/SyntaxMode.js delete mode 100644 js/editor.js delete mode 100644 js/index.js delete mode 100644 js/newfileplugin.js delete mode 100644 js/public-share.js delete mode 100644 js/sidebarpreview.js delete mode 100644 js/supported_mimetypes.json create mode 100644 src/TextEditor.vue create mode 100644 src/main.js diff --git a/.gitignore b/.gitignore index 19f62871..115370c1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ /tests/coverage* /tests/clover.xml node_modules -build +js/* /vendor/ diff --git a/js/ImportAce.js b/js/ImportAce.js deleted file mode 100644 index 37d1e774..00000000 --- a/js/ImportAce.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * @copyright Copyright (c) 2019 Julius Härtl - * - * @author Julius Härtl - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -export default function() { - return import(/* webpackChunkName: "ace" */'brace') -} diff --git a/js/SyntaxMode.js b/js/SyntaxMode.js deleted file mode 100644 index c7879bad..00000000 --- a/js/SyntaxMode.js +++ /dev/null @@ -1,178 +0,0 @@ -const filetype = {}; -// add file extensions like this: filetype["extension"] = "filetype": -filetype["bat"] = "batchfile"; -filetype["cmd"] = "batchfile"; -filetype["h"] = "c_cpp"; -filetype["c"] = "c_cpp"; -filetype["clj"] = "clojure"; -filetype["coffee"] = "coffee"; // coffescript can be compiled to javascript -filetype["cpp"] = "c_cpp"; -filetype["cs"] = "csharp"; -filetype["css"] = "css"; -filetype["groovy"] = "groovy"; -filetype["htm"] = "html"; -filetype["html"] = "html"; -filetype["tt"] = "html"; -filetype["java"] = "java"; -filetype["js"] = "javascript"; -filetype["jsm"] = "javascript"; -filetype["json"] = "json"; -filetype["latex"] = "latex"; -filetype["tex"] = "latex"; -filetype["less"] = "less"; -filetype["ly"] = "latex"; -filetype["ily"] = "latex"; -filetype["lua"] = "lua"; -filetype["markdown"] = "markdown"; -filetype["md"] = "markdown"; -filetype["mdown"] = "markdown"; -filetype["mdwn"] = "markdown"; -filetype["mkd"] = "markdown"; -filetype["ml"] = "ocaml"; -filetype["mli"] = "ocaml"; -filetype["pl"] = "perl"; -filetype["php"] = "php"; -filetype["ps1"] = "powershell"; -filetype["py"] = "python"; -filetype["rb"] = "ruby"; -filetype["scad"] = "scad"; // seems to be something like 3d model files printed with e.g. reprap -filetype["scala"] = "scala"; -filetype["scss"] = "scss"; // "sassy css" -filetype["sh"] = "sh"; -filetype["sql"] = "sql"; -filetype["svg"] = "svg"; -filetype["textile"] = "textile"; // related to markdown -filetype["xml"] = "xml"; - -const loaders = {}; -// add loader like this: loaders["filetype"] = () => require("promise?brace/mode/filetype"): - -// note that these require statements can't be generated from the filetypes dynamically -// to make sure webpack can analyse our dependencies -loaders["batchfile"] = () => import( - /* webpackChunkName: "bat-mode" */ - "brace/mode/batchfile" - ); -loaders["c_cpp"] = () => import( - /* webpackChunkName: "h-mode" */ - "brace/mode/c_cpp" - ); -loaders["coffee"] = () => import - /* webpackChunkName: "coffee-mode" */( - "brace/mode/coffee" - ); -loaders["csharp"] = () => import( - /* webpackChunkName: "cs-mode" */ - "brace/mode/csharp" - ); -loaders["clojure"] = () => import( - /* webpackChunkName: "clj-mode" */ - "brace/mode/clojure" - ); -loaders["css"] = () => import( - /* webpackChunkName: "css-mode" */ - "brace/mode/css" - ); -loaders["groovy"] = () => import - /* webpackChunkName: "groovy-mode" */( - "brace/mode/groovy" - ); -loaders["html"] = () => import( - /* webpackChunkName: "html-mode" */ - "brace/mode/html" - ); -loaders["java"] = () => import( - /* webpackChunkName: "java-mode" */ - "brace/mode/java" - ); -loaders["javascript"] = () => import( - /* webpackChunkName: "js-mode" */ - "brace/mode/javascript" - ); -loaders["json"] = () => import( - /* webpackChunkName: "json-mode" */ - "brace/mode/json" - ); -loaders["latex"] = () => import( - /* webpackChunkName: "latex-mode" */ - "brace/mode/latex" - ); -loaders["less"] = () => import( - /* webpackChunkName: "less-mode" */ - "brace/mode/less" - ); -loaders["lua"] = () => import( - /* webpackChunkName: "lua-mode" */ - "brace/mode/lua" - ); -loaders["markdown"] = () => import( - /* webpackChunkName: "markdown-mode" */ - "brace/mode/markdown" - ); -loaders["ocaml"] = () => import( - /* webpackChunkName: "ml-mode" */ - "brace/mode/ocaml" - ); -loaders["perl"] = () => import( - /* webpackChunkName: "pl-mode" */ - "brace/mode/perl" - ); -loaders["php"] = () => import( - /* webpackChunkName: "php-mode" */ - "brace/mode/php" - ); -loaders["powershell"] = () => import( - /* webpackChunkName: "ps1-mode" */ - "brace/mode/powershell" - ); -loaders["python"] = () => import( - /* webpackChunkName: "py-mode" */ - "brace/mode/python" - ); -loaders["ruby"] = () => import( - /* webpackChunkName: "rb-mode" */ - "brace/mode/ruby" - ); -loaders["scad"] = () => import( - /* webpackChunkName: "scad-mode" */ - "brace/mode/scad" - ); // seems to be something like 3d model files printed with e.g. reprap -loaders["scala"] = () => import( - /* webpackChunkName: "scala-mode" */ - "brace/mode/scala" - ); -loaders["scss"] = () => import( - /* webpackChunkName: "scss-mode" */ - "brace/mode/scss"); // "sassy css" -loaders["sh"] = () => import( - /* webpackChunkName: "sh-mode" */ - "brace/mode/sh" - ); -loaders["sql"] = () => import( - /* webpackChunkName: "sql-mode" */ - "brace/mode/sql" - ); -loaders["svg"] = () => import( - /* webpackChunkName: "svg-mode" */ - "brace/mode/svg" - ); -loaders["textile"] = () => import( - /* webpackChunkName: "textile-mode" */ - "brace/mode/textile"); // related to markdown -loaders["xml"] = () => import( - /* webpackChunkName: "xml-mode" */ - "brace/mode/xml" - ); - -export function getSyntaxMode (extension) { - const type = filetype[extension]; - if (type) { - // Then it must be in the array, so load the custom syntax mode - // Set the syntax mode - return loaders[type]().then(() => { - return type; - }); - } - - return $.when(); -} diff --git a/js/editor.js b/js/editor.js deleted file mode 100644 index 3c783efa..00000000 --- a/js/editor.js +++ /dev/null @@ -1,603 +0,0 @@ -import {getSyntaxMode} from './SyntaxMode'; -import importAce from './ImportAce'; -import escapeHTML from 'escape-html' -import {generateUrl} from '@nextcloud/router'; - -/** @type array[] supportedMimeTypes */ -const supportedMimeTypes = require('./supported_mimetypes.json'); - -let ace; -export const Texteditor = { - - /** - * Holds the editor container - */ - $container: null, - - /** - * Holds the editor element ID - */ - editor: 'filestexteditor', - - /** - * Stores info on the file being edited - */ - file: { - edited: false, - mtime: null, - dir: null, - name: null, - writeable: null, - mime: null, - size: null - }, - - /** - * Stored the saving state - */ - saving: false, - - /** - * Current files app context - */ - currentContext: null, - - /** - * Stores the autosave timer - */ - saveTimer: null, - - /** - * Stores the old page title - */ - oldTitle: null, - - /** - * Stores the timeout for the saving message - */ - saveMessageTimeout: null, - - /** - * preview plugins by mimetype - */ - previewPlugins: {}, - - registerPreviewPlugin: function (mimeType, plugin) { - this.previewPlugins[mimeType] = plugin; - }, - - previewPluginsLoaded: {}, - - /** - * preview element - */ - preview: null, - - previewPluginOnChange: null, - - /** - * Save handler, triggered by the button, or keyboard - */ - _onSaveTrigger: function () { - // Don't save if not edited - if (!Texteditor.file.edited) { - return; - } - // Don't try to save twice - if (Texteditor.saving) { - return; - } else { - Texteditor.saving = true; - Texteditor.file.edited = false; - } - - // Can any fade outs on the saving message - clearTimeout(Texteditor.saveMessageTimeout); - - // Set the saving status - var $message = $('#editor_controls').find('small.saving-message'); - $message.text(t('files_texteditor', 'Saving …')) - .show(); - // Send to server - Texteditor.saveFile( - window.aceEditor.getSession().getValue(), - Texteditor.file, - function (data) { - // Yay - if (Texteditor.file.edited == false) { - document.title = Texteditor.file.name + ' - ' + Texteditor.oldTitle; - $('small.unsaved-star').css('display', 'none'); - } - Texteditor.file.mtime = data.mtime; - Texteditor.file.size = data.size; - - $message.text(t('files_texteditor', 'Saved!')); - Texteditor.saveMessageTimeout = setTimeout(function () { - $('small.saving-message').fadeOut(200); - }, 2000); - }, - function (message) { - // Boo - if (typeof message == 'undefined') { - $('small.saving-message').text(t('files_texteditor', 'Failed!')); - } else { - $('small.saving-message').text(message); - } - Texteditor.saveMessageTimeout = setTimeout(function () { - $('small.saving-message').fadeOut(200); - }, 5000); - Texteditor.file.edited = true; - } - ); - Texteditor.saving = false; - window.aceEditor.focus(); - }, - - /** - * Handles on close button click - */ - _onCloseTrigger: function () { - // Hide or close? - if (!Texteditor.file.edited) { - Texteditor.closeEditor(); - } else { - // Trick the autosave attempt into thinking we have no changes - Texteditor.file.edited = false; - // Hide the editor - Texteditor.hideEditor(); - // Try to save - Texteditor.saveFile( - window.aceEditor.getSession().getValue(), - Texteditor.file, - function () { - OC.Notification.showTemporary(t( - 'files_texteditor', - 'Saved' - ) - ); - // Remove the editor - Texteditor.closeEditor(); - }, - function () { - OC.Notification.showTemporary(t( - 'files_texteditor', - 'There was a problem saving your changes. Click to resume editing.' - )); - $('#notification').data('reopeneditor', true).on( - 'click', - Texteditor._onReOpenTrigger - ); - Texteditor.file.edited = true; - } - ); - } - }, - - /** - * Handles the trigger or re open editor - */ - _onReOpenTrigger: function () { - if ($('#notification').data('reopeneditor') == true) { - document.title = Texteditor.file.name + ' - ' + Texteditor.oldTitle; - Texteditor.$container.show(); - } - }, - - /** - * Handles the FileAction click event - */ - _onEditorTrigger: function (filename, context) { - this.currentContext = context; - this.file.name = filename; - this.file.dir = context.dir; - this.fileList = context.fileList; - importAce().then((_ace) => { - require('brace/ext/searchbox'); - ace = _ace; - this.loadEditor( - Texteditor.$container, - Texteditor.file - ); - history.pushState({ - file: filename, - dir: context.dir - }, 'Editor', '#filestexteditor'); - }); - }, - - /** - * Handler for edits detected - */ - _onEdit: function () { - if (!Texteditor.file.edited) { - Texteditor.file.edited = true; - if (!Texteditor.saving) { - Texteditor._onUnsaved(); - } - } - if (this.previewPluginOnChange) { - var text = window.aceEditor.getSession().getValue(); - this.previewPluginOnChange(text, this.preview); - } - }, - - /** - * Handler when unsaved work is detected - */ - _onUnsaved: function () { - document.title = '* ' + Texteditor.file.name + ' - ' + Texteditor.oldTitle; - $('small.unsaved-star').css('display', 'inline-block'); - }, - - /** - * Setup on page load - */ - initialize: function (container) { - // Don't load if not in the files app TODO: Fix for sharing - if (!$('#content.app-files').length) { - return; - } - this.$container = container; - this.registerFileActions(); - this.oldTitle = document.title; - }, - - /** - * Registers the file actions - */ - registerFileActions: function () { - supportedMimeTypes.forEach((mime) => { - OCA.Files.fileActions.registerAction({ - name: 'edit_texteditor', - displayName: t('files_texteditor', 'Edit in plain text editor'), - mime: mime, - actionHandler: this._onEditorTrigger.bind(this), - permissions: OC.PERMISSION_READ, - iconClass: 'icon-edit' - }); - OCA.Files.fileActions.setDefault(mime, 'edit_texteditor'); - }); - }, - - /** - * Actually fire up the editor in a container - */ - loadEditor: function (container, file) { - var _self = this; - // Insert the editor into the container - container.html( - '
' - + '
' - + '
' - + '
'); - $('#content').append(container); - - // Get the file data - this.loadFile( - file.dir, - file.name, - function (file, data) { - // Success! - // Sort the title - document.title = file.name + ' - ' + Texteditor.oldTitle; - // Load ace - $('#' + _self.editor).text(data); - // Remove loading - $('#editor_container').removeClass('icon-loading'); - // Configure ace - _self.configureACE(file); - // Show the controls - _self.loadControlBar(file); - window.aceEditor.getSession().on('change', _self.setupAutosave); - _self.bindVisibleActions(); - window.aceEditor.focus(); - - if (_self.previewPlugins[file.mime]) { - _self.preview = container.find('#preview'); - _self.preview.addClass(file.mime.replace('/', '-')); - if (window.aceEditor.getReadOnly()){ - container.find('#editor_container').addClass('onlyPreview'); - } - container.find('#editor_container').addClass('hasPreview'); - _self.previewPluginOnChange = _.debounce(function (text, element) { - _self.loadPreviewPlugin(file.mime).then(function () { - _self.previewPlugins[file.mime].preview(text, element); - }); - }, 200); - var text = window.aceEditor.getSession().getValue(); - _self.previewPluginOnChange(text, _self.preview); - setTimeout(function () { - window.aceEditor.resize(); - }, 500); - _self.loadPreviewControlBar(); - } else { - _self.previewPluginOnChange = null; - } - }, - function (message) { - // Oh dear - OC.dialogs.alert(message, t('files_texteditor', 'An error occurred!')); - _self.closeEditor(); - }); - }, - - loadPreviewPlugin: function (mime) { - if (this.previewPluginsLoaded[mime]) { - return $.Deferred().resolve().promise(); - } - this.previewPluginsLoaded[mime] = true; - var plugin = this.previewPlugins[mime]; - return $.when(plugin.init()); - }, - - /** - * Load the editor control bar - */ - loadControlBar: function (file) { - var html = - '' + escapeHTML(file.name) + '' - + '' - + '' - + '' - + ''; - var controlBar = $('
').html(html); - $('#editor_wrap').before(controlBar); - this.setFilenameMaxLength(); - this.bindControlBar(); - - }, - - setPreviewMode: function (mode) { - var container = $('#app-content-texteditor'); - var controlBar = $('#preview_editor_controls'); - controlBar.find('button').removeClass('active'); - controlBar.find('button[data-type="' + mode + '"]').addClass('active'); - switch (mode) { - case 'mixed': - container.find('#editor_container').addClass('hasPreview'); - container.find('#editor_container').removeClass('onlyPreview'); - break; - case 'text': - container.find('#editor_container').removeClass('hasPreview'); - container.find('#editor_container').removeClass('onlyPreview'); - break; - case 'image': - container.find('#editor_container').addClass('hasPreview'); - container.find('#editor_container').addClass('onlyPreview'); - break; - } - setTimeout(function () { - window.aceEditor.resize(); - }, 500); - }, - - loadPreviewControlBar: function () { - var makeButton = function (type, tooltip, active) { - var button = $('