From: soufiane Date: Mon, 21 Jul 2025 16:59:36 +0000 (+0200) Subject: wip #7634 @6:00 X-Git-Url: http://git.cubedesigners.com/?a=commitdiff_plain;h=73d109efaa9fd584c5309bc0cace507e6cb7cb58;p=fluidbook-toolbox.git wip #7634 @6:00 --- diff --git a/app/Http/Controllers/Admin/Operations/FluidbookPublication/MarkdownOperation.php b/app/Http/Controllers/Admin/Operations/FluidbookPublication/MarkdownOperation.php index feaa43cde..11af21df2 100644 --- a/app/Http/Controllers/Admin/Operations/FluidbookPublication/MarkdownOperation.php +++ b/app/Http/Controllers/Admin/Operations/FluidbookPublication/MarkdownOperation.php @@ -6,13 +6,14 @@ use App\Models\FluidbookPublication; use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; use Illuminate\Support\Str; +use Cubist\Util\Files\Files; trait MarkdownOperation { protected function setupMarkdownRoutes($segment, $routeName, $controller) { Route::match(['get'], $segment . '/{id}/edit/markdown', $controller . '@markdown')->name('fluidbook_markdowneditor'); - Route::match(['post'], $segment . '/{id}/markdown', $controller . '@getFilesById'); + Route::match(['get'], $segment . '/{id}/markdown', $controller . '@getFilesById'); } public function markdown($id) @@ -22,17 +23,20 @@ trait MarkdownOperation } $token = Str::random(10); + $files = $this->getFilesById($id); - return view('fluidbook_publication.markdown_editor', ['version' => 'stable', 'id' => $id, 'fluidbook' => FluidbookPublication::find($id), 'access' => "", 'token' => $token]); + return view('fluidbook_publication.markdown_editor', ['files' => $files, 'version' => 'stable', 'id' => $id, 'fluidbook' => FluidbookPublication::find($id), 'access' => "", 'token' => $token]); } - public static function getFilesById(Request $request,$id) + public function getFilesById($id) { - $p = $request->page; - $path = public_path("/markdown_files/$id/p$p.md"); + //$p = $request->page;; + $files = Files::getRecursiveDirectoryIterator(public_path("/markdown_files/$id"), true); - if(!file_exists($path)) return false; + return $files; - return file_get_contents(public_path("/markdown_files/$id/p$p.md")); + //if(!file_exists($path)) return false; + + //return file_get_contents(public_path("/markdown_files/$id/p$p.md")); } } diff --git a/resources/markdowneditor/js/markdowneditor.js b/resources/markdowneditor/js/markdowneditor.js index 730eadbad..f7903601a 100644 --- a/resources/markdowneditor/js/markdowneditor.js +++ b/resources/markdowneditor/js/markdowneditor.js @@ -1,5 +1,6 @@ import Editor from '@toast-ui/editor'; import MarkdowneditorToolbar from "./markdowneditor.toolbar"; +import MarkdowneditorUndo from "./markdowneditor.undo"; window.$ = window.jQuery = require('jquery'); $.ajaxSetup({ @@ -17,17 +18,20 @@ function MarkdownEditor() { this.noCache = '?t=' + (new Date(FLUIDBOOK_DATA.composition_updated_at)).getTime(); this.currentPage = Math.max(1,parseInt(window.location.hash.substring(1))) || 1; this.editor = null; + this.contentMarkdown = null; this.init() } MarkdownEditor.prototype = { init: function() { - this.toolbar = new MarkdowneditorToolbar(this); + new MarkdowneditorToolbar(this); + this.undo = new MarkdowneditorUndo(this); const $this = this this.initIcons(); this.markdown(); this.changePage(); + this.undo.initState(); $(window).on('hashchange', function () { /*if ($this.maskHashEvent) { @@ -72,6 +76,11 @@ MarkdownEditor.prototype = { $(".handle").removeClass("dragging") $("body").removeClass("user-select-none") }) + + $(document).on('keyup', function (e) { + $this.undo.pushState() + console.log($this.undo.states) + }) }, initIcons: function () { @@ -81,10 +90,7 @@ MarkdownEditor.prototype = { }, loadPage: function() { - if (!this.single) { - this.loadPageHtml(this.currentPage); - } - + this.loadPageHtml(this.currentPage); this.setContentMarkdown() }, @@ -132,17 +138,22 @@ MarkdownEditor.prototype = { return res; }, + getCurrentPage: function () { + return this.currentPage; + }, + setContentMarkdown: function() { const $this = this - $.ajax({ - url: '/fluidbook-publication/' + FLUIDBOOK_DATA.id +'/markdown', - type: 'post', - data: {page:this.currentPage}, - success: function (response) { - $this.editor.setMarkdown(response) - $this.editor.moveCursorToStart(true) - } - }); + if(this.undo.states[this.getCurrentPage()] !== undefined || this.undo.states[this.getCurrentPage()] === null) { + const state = this.undo.states[this.getCurrentPage()] + const lastKey = state.length; + this.editor.setMarkdown(state[lastKey]) + this.editor.moveCursorToStart(true) + }else { + this.contentMarkdown = MARKDOWN_DATA["p"+this.currentPage] + this.editor.setMarkdown(this.contentMarkdown) + this.editor.moveCursorToStart(true) + } }, markdown: function() { @@ -162,6 +173,11 @@ MarkdownEditor.prototype = { this.editor.getMarkdown(); }, + setCurrentState: function (state) { + this.editor.setMarkdown(state) + this.editor.moveCursorToStart(true) + }, + changePage: function(page) { if (page === undefined) { let h = window.location.hash; diff --git a/resources/markdowneditor/js/markdowneditor.save.js b/resources/markdowneditor/js/markdowneditor.save.js new file mode 100644 index 000000000..610e3bc2a --- /dev/null +++ b/resources/markdowneditor/js/markdowneditor.save.js @@ -0,0 +1,92 @@ +function MarkdowneditorSave(linkeditor) { + this.linkeditor = linkeditor; + this.init(); +} + +MarkdowneditorSave.prototype = { + init: function () { + let $this = this; + + this.automaticSaveFrequency = 1000 * 5 * 60; + this.unsavedChanges = false; + this.automaticSaveTimeout = null; + this.runningAutomaticSaveTimeout = false; + + $(window).on('beforeunload', function () { + if ($this.unsavedChanges) { + return TRANSLATIONS.warning_unsaved_changes; + } + }) + }, + + hasChanged: function () { + let $this = this; + this.unsavedChanges = true; + if (this.runningAutomaticSaveTimeout === false) { + this.runningAutomaticSaveTimeout = true; + this.automaticSaveTimeout = setTimeout(function () { + $this.automaticSave(); + }, this.automaticSaveFrequency); + } + }, + + saveIfUnsavedChanges: function (message, notify, callback) { + if (this.unsavedChanges) { + this.save(message, false, function () { + setTimeout(function () { + callback(); + }, 1000); + }); + } else { + callback(); + } + }, + + save: function (message, notify, callback) { + if (notify === undefined) { + notify = true; + } + if (callback === undefined) { + callback = function () { + + }; + } + var $this = this; + if (message === undefined) { + message = TRANSLATIONS.manual_save_message; + } + + $.ajax({ + url: '/fluidbook-publication/' + FLUIDBOOK_DATA.id + '/save/links', method: 'post', data: { + _method: 'put', + message: message, + rulers: JSON.stringify(window.RULERS), + links: JSON.stringify(window.LINKS), + }, + success: function (data) { + if (notify) { + $this.linkeditor.notification(TRANSLATIONS.success_save); + } + clearTimeout($this.automaticSaveTimeout); + $this.unsavedChanges = false; + $this.runningAutomaticSaveTimeout = false; + + window.ASSETS = data.assets; + $this.linkeditor.versions.setVersions(data.versions); + callback(); + }, + error: function (jqXHR, status, error) { + $this.linkeditor.hasChanged(); + $this.linkeditor.notification(TRANSLATIONS.error_save + ' : ' + error, 'error'); + }, + }); + + $this.linkeditor.links.loadFontSize(); + }, + + automaticSave: function () { + this.save(TRANSLATIONS.automatic_save_message); + }, +}; + +export default MarkdowneditorSave; diff --git a/resources/markdowneditor/js/markdowneditor.undo.js b/resources/markdowneditor/js/markdowneditor.undo.js new file mode 100644 index 000000000..eff3553d3 --- /dev/null +++ b/resources/markdowneditor/js/markdowneditor.undo.js @@ -0,0 +1,133 @@ +function MarkdowneditorUndo(markdowneditor) { + this.markdowneditor = markdowneditor; + this.ignoreStatesChanges = false; + this.states = []; + this.indexes = []; + this.init(); +} + +MarkdowneditorUndo.prototype = { + init: function () { + }, + + initState: function () { + if (this.states[this.markdowneditor.getCurrentPage()] === undefined || this.states[this.markdowneditor.getCurrentPage()] === null) { + this.indexes[this.markdowneditor.getCurrentPage()] = 0; + this.pushState(); + } + }, + + _states: function () { + let redo = false; + let undo = false; + let nb = this.states[this.markdowneditor.getCurrentPage()].length; + let idx = this.indexes[this.markdowneditor.getCurrentPage()]; + if (nb > 1) { + if (idx < nb) { + redo = true; + } + if (idx > 1) { + undo = true; + } + } + return {redo: redo, undo: undo, index: idx, nb: nb}; + }, + + updateIconsStates: function () { + let s = this._states(); + if (s.redo) { + $('[data-icon=redo]').removeClass('disabled'); + } else { + $('[data-icon=redo]').addClass('disabled'); + } + + if (s.undo) { + $('[data-icon=undo]').removeClass('disabled'); + } else { + $('[data-icon=undo]').addClass('disabled'); + } + }, + + canRedo: function () { + return this._states().redo; + }, + + canUndo: function () { + return this._states().undo; + }, + + + pushState: function () { + if (this.ignoreStatesChanges) { + console.log('ignore states changes'); + return; + } + + let index = this.indexes[this.markdowneditor.getCurrentPage()]; + if (index === 0) { + this.states[this.markdowneditor.getCurrentPage()] = [ + this.markdowneditor.contentMarkdown + ]; + console.log('ok',this.states,this.markdowneditor.editor.getMarkdown(),'ok') + } + + let cs = this.markdowneditor.editor.getMarkdown(); + let ps = this.states[this.markdowneditor.getCurrentPage()][index - 1]; + if (ps == cs) { + console.log('skipped : no change'); + return; + } + + if (index > 0 && index < this.states[this.markdowneditor.getCurrentPage()].length) { + this.states[this.markdowneditor.getCurrentPage()] = this.states[this.markdowneditor.getCurrentPage()].slice(0, index); + } + this.states[this.markdowneditor.getCurrentPage()].push(cs); + this.indexes[this.markdowneditor.getCurrentPage()]++; + + //console.log('push current index', index, 'states length', this.states[this.markdowneditor.getCurrentPage()].length); + + this.updateIconsStates(); + }, + + undo: function () { + if (!this.canUndo()) { + return; + } + let index = this.indexes[this.markdowneditor.getCurrentPage()]; + index--; + let state = this.states[this.markdowneditor.getCurrentPage()][index - 1]; + this.ignoreStatesChanges = true; + this.markdowneditor.setCurrentState(state); + var $this = this; + setTimeout(function () { + $this.ignoreStatesChanges = false; + }, 500); + this.indexes[this.markdowneditor.getCurrentPage()] = index; + + //console.log('undo : current index', index, 'states length', this.states[this.markdowneditor.getCurrentPage()].length); + + this.updateIconsStates(); + }, + + redo: function () { + if (!this.canRedo()) { + return; + } + let index = this.indexes[this.markdowneditor.getCurrentPage()]; + let state = this.states[this.markdowneditor.getCurrentPage()][index]; + this.ignoreStatesChanges = true; + this.markdowneditor.setCurrentState(state); + var $this = this; + setTimeout(function () { + $this.ignoreStatesChanges = false; + }, 500); + index++; + this.indexes[this.markdowneditor.getCurrentPage()] = index; + //console.log('redo : current index', index, 'states length', this.states[this.markdowneditor.getCurrentPage()].length); + + this.updateIconsStates(); + } +} + + +export default MarkdowneditorUndo; diff --git a/resources/views/fluidbook_publication/markdown_editor.blade.php b/resources/views/fluidbook_publication/markdown_editor.blade.php index ebc6f7620..1af5f0535 100644 --- a/resources/views/fluidbook_publication/markdown_editor.blade.php +++ b/resources/views/fluidbook_publication/markdown_editor.blade.php @@ -4,6 +4,13 @@ $fbdata['settings']['pages']=$fbdata['pages']=$fluidbook->getPagesNumber(); $fbdata['settings']['imageFormat']=$fluidbook->getImageFormat(); $fbdata['page_dimensions']=[]; + + $collection = collect($files); + $filesdata = $collection->flatMap(function($item) { + $filename = $item->getFilename(); + return [$filename => file_get_contents($item)]; + } ); + $sorted = $filesdata->sortKeys(); @endphp @extends('layouts.markdowneditor') @@ -64,14 +71,16 @@ @endsection -@push('markdown_styles') - - -@endpush @push('markdown_scripts') @@ -80,3 +89,8 @@ @endpush +@push('markdown_styles') + + +@endpush